第2章 顺序表及其顺序存储

第2章 顺序表及其顺序存储

目录
  • 一、线性表
  • 二、顺序表
    • 2.1 顺序表的基本概念及描述
    • 2.2 顺序表的实现
      • 2.2.1 顺序表的存储结构
      • 2.2.2 顺序表的插入操作(程序)
      • 2.2.3 顺序表的删除操作(程序)
  • 三、栈
    • 3.1 栈的基本概念及描述
    • 3.2 顺序栈及其实现
    • 3.3 栈的应用之一(括号匹配)
    • 3.4 栈的应用之二(算术表达式求值)
      • 3.4.1 中缀表达式转换为后缀表达式步骤
  • 四、队列
    • 4.1 队列的基本概念及描述
    • 4.2 顺序队列及其实现
    • 4.3 顺序循环队列及其实现
  • 五、程序设计习题
    • 5.1 顺序表值为x的结点的个数(程序)
    • 5.2 顺序表倒置(程序)
    • 5.3 有序顺序表插入结点并仍然有序-真题(程序)

一、线性表

  1. 线性表:属于线性结构。有且仅有一个开始结点,有且仅有一个终端结点,其他结点为内部结点。
  2. 线性表的存储:顺序存储 or 链式存储

二、顺序表

2.1 顺序表的基本概念及描述

  1. 顺序表:线性表采用顺序存储的方式
  2. 注:每个结点占用\(len\)个内存单元,\(location(k_i)\)为顺序表中第\(i\)个结点\(k_i\)所占内存空间的第\(1\)个单元的地址
    1. \(location(k_i) = location(k_i) + len\)
    2. \(location(k_i) = location(k_1) + (i-1)len\)

2.2 顺序表的实现

  1. 注:数组中下标为\(i\)的元素对应的是顺序表中第\(i+1\)个结点
  2. 顺序表的常用操作(简单且大概率不考,因此无代码展示,后不提醒)
    1. 顺序表的初始化
    2. 顺序表后部进行插入操作
    3. 打印顺序表的各结点值
    4. 判断顺序表是否为空
    5. 查找顺序表中值为x的结点位置
    6. 取得顺序表中第i个结点的值

2.2.1 顺序表的存储结构

#define MAXSIZE 100
typedef int datatype;
typedef struct{
  datatype a[MAZXSIZE];
  int size;
}sequence_list;

2.2.2 顺序表的插入操作(程序)

  1. 将值为\(x\)的结点插入到第\(i\)个位置的步骤:
    1. 判断顺序表是否是满的
    2. 判断指定的位置是否不存在
    3. \(k_i,\cdots,k_{n-1}\)这些结点对应的数组元素依次后移一个位置,空出\(k_i\)原先的位置存放新插入的结点。(从后往前移)
    4. 将值为\(x\)的结点插入到第\(i\)个位置
    5. 顺序表的长度加\(1\)
#define MAXSIZE 100
typedef int datatype;
typedef struct{
  datatype a[MAZXSIZE];
  int size;
}sequence_list;

void insert(sequence_list *slt, datatype x, int position)
{
  int i;
  if (slt->size == MAXSIZE)
  {
    printf("\n顺序表是满的!没法插入!");
    exit(1);
  }
  if (position < 0 || position > slt->size) // 如size为10,slt->a[i-1=9]存在,既可以不用>=
  {
    printf("\n指定的插入位置不存在!");
    exit(1);
  }
  for (i = slt->size; i > position; i--)
  {
    slt->a[i] = slt->a[i-1];
  }
  slt->a[position] = x;
  slt->size++;
}

时间复杂度:\(O(n)\)

2.2.3 顺序表的删除操作(程序)

  1. 删除顺序表中第\(i\)个结点的步骤:
    1. 判断顺序表是否为空
    2. 判断删除位置是否存在
    3. 将顺序表中的\(k_{i+1},\cdots,k_{n-1}\)元素依次前移一个位置。(从前往后移)
    4. 顺序表的长度减\(1\)
#define MAXSIZE 100
typedef int datatype;
typedef struct{
  datatype a[MAZXSIZE];
  int size;
}sequence_list;

void dele(sequence_list *slt, int position)
{
  int i;
  if (slt->size == 0)
  {
    pringf("\n顺序表是空的!");
    exit(1);
  }
  if (position < 0 || position >= slt->size) // 如size为10,postion为10,slt->a[i+1=11]不存在,即不可以>
  {
    printf("\n指定的删除位置不存在!");
    exit(1);
  }
  for (i = position; i < slt->size-1; i++) // 循环到倒数第二个元素即可,因为后面是a[i+1]
  {
    slt->a[i] = slt->a[i+1];
  }
  slt->size--;
}

时间复杂度:\(O(n)\)

三、栈

3.1 栈的基本概念及描述

  1. 栈:特殊的线性表。插入运算和删除运算均在线性表的同一端进行
  2. 栈顶:进行插入(进栈)和删除(出栈)的一端
  3. 栈底:不进行操作的一端
  4. 栈的性质:后进先出(先进后出)

3.2 顺序栈及其实现

  1. 栈(顺序存储)的常用操作:
    1. 栈的存储结构
    2. 栈初始化
    3. 判断栈是否为空
    4. 取得栈顶结点值
    5. 栈的插入操作
    6. 栈的删除操作

3.3 栈的应用之一(括号匹配)

  1. 括号匹配解决步骤:
    1. 从左向右扫描表达式
    2. 遇到开括号则将遇到的开括号存放于栈中
    3. 遇到闭括号则查看是否与栈顶结点匹配。匹配删除栈顶结点,不匹配说明表达式中的括号是不匹配的(结束
    4. 扫描整个表达式后,栈是空的,说明表达式中的括号是匹配的;否则是不匹配的(结束

3.4 栈的应用之二(算术表达式求值)

  1. 中缀表达式:操作符处于两个操作数之间
  2. 前缀表达式:操作符处于两个操作数之前
  3. 后缀表达式:操作符处于两个操作数之后
  4. 后缀表达式求值:每遇到一个操作符,则将前面的两个操作数执行相应的操作
  5. 后缀表达式求解步骤:
    1. 把遇到的操作数暂存于一个栈中
    2. 遇到操作符,则从栈中取出两个操作数执行相应的操作,并将结果压入栈中
    3. 直到对后缀表达式中最后一个操作符处理完
    4. 最后压入栈中的树就是后缀表达式的计算结果

3.4.1 中缀表达式转换为后缀表达式步骤

  1. 从左到右扫描中缀表达式,如果是数字字符和圆点“.”,直接写入后缀表达式

  2. 遇到开括号,将开括号压入栈中,遇到匹配的闭括号,将栈中元素弹出并放入后缀表达式,直到栈顶元素为匹配的开括号时,弹出该开括号

  3. 遇到的是操作符:

    1. 遇到的操作符优先级<=栈顶元素,弹出栈顶元素放入后缀表达式(循环判断)
    2. 遇到的操作符优先级>栈顶元素,将遇到的操作符压入栈中
  4. 重复上述步骤,直到遇到结束标记“#”,弹出栈中的所有元素并放入后缀表达式数组中

  5. 操作符优先级:\(\#,(,+-,*/\)

-1 0 1 2
# ( +- */

四、队列

4.1 队列的基本概念及描述

  1. 队列:一种特殊的线性表。插入(进队)和删除(出队)操作分别在表的两端进行
  2. 队尾:插入的一端
  3. 对首:删除的一端
  4. 队列的性质:先进先出

4.2 顺序队列及其实现

  1. 队列(顺序存储)的常用操作:
    1. 队列的顺序结构
    2. 队列初始化
    3. 判断队列是否为空
    4. 打印队列的结点值
    5. 取得队列的队首结点值
    6. 队列的插入操作
    7. 队列的删除操作

4.3 顺序循环队列及其实现

  1. 普通队列的列满状态指标:\(rear = MAXSIZE\)
  2. 循环队列的作用:在普通队列处于列满状态时,利用数组前部的空闲位置
  3. 循环队列的列满状态指标:
    1. 设置一个标志:由于\(rear\)\(1\)使\(rear = front\),为队满;由于\(front\)\(1\)使\(rear = front\),队空
    2. 牺牲一个数组元素空间:\((rear + 1) \% MAXSIZE = front\),队满;\(rear = front\),队空
  4. 循环队列(顺序存储)的常用操作(相比较普通队列在指针增\(1\)中增加一个取模操作):
    1. 循环队列的插入操作
    2. 循环队列的删除操作

五、程序设计习题

5.1 顺序表值为x的结点的个数(程序)

设计一个算法,求顺序表中值为x的结点的个数

程序步骤:

  1. 设定初始值\(c\)计数
  2. 每找到一个值为\(x\)的结点,计数加\(1\)
  3. 返回\(c\)
// 顺序表的存储结构定义如下(文件名seqlist.h)
#include 
#define N 100 // 预定义最大的数据域空间
typedef int datatype; // 假设数据类型为整型
typedef struct{
  datatype data[N]; // 此处假设数据元素只包含一个整型的关键字域
  int length; // 线性表长度
}seqlist; // 预定义的顺序表类型

// 算法countx(L, x)用于求顺序表L中值为x的结点的个数
int countx(seqlist *L, datatype x)
{
  int c = 0;
  int i;
  for (i = 0; i < L->length; i++)
  {
    if (L->data[i] == x) c++;
  }
	return c;
}

5.2 顺序表倒置(程序)

设计一个算法,将一个顺序表倒置。即,如果顺序表各个结点值存储在一维数组\(a\)中,倒置的结果是使得数组\(a\)中的\(a[o]\)等于原来的最后一个元素,\(a[1]\)等于原来的倒数第\(2\)个元素,\(\cdots\)\(a\)的最后一个元素等于原来的第一个元素

程序步骤:

  1. 定位最后一个元素
  2. \(t\)保留第个一元素的值,并且第一个元素和最后一个元素的值交换
  3. 循环到最后一个元素
// 顺序表的存储结构定义如下(文件名seqlist.h)
#include 
#define N 100 // 预定义最大的数据域空间
typedef int datatype; // 假设数据类型为整型
typedef struct{
  datatype data[N]; // 此处假设数据元素只包含一个整型的关键字域
  int length; // 线性表长度
}seqlist; // 预定义的顺序表类型

// 算法verge(L)用于顺序表倒置
void verge(seqlist *L)
{
  int t, i, j;
  i = 0;
  j = L->length-1;
  while (i < j)
  {
    t = L->data[i];
    L->data[i++] = L->data[j];
    L->data[j--] = t;
  }
}

5.3 有序顺序表插入结点并仍然有序-真题(程序)

已知一个顺序表中的各结点值是从小到大有序的,设计一个算法,插入一个值为\(x\)的结点,使顺序表中的结点仍然是从小到大有序

程序步骤:

  1. 从最后一个元素开始与\(x\)比较大小,元素值大于该\(x\)值,则往后挪一个位置
  2. 找到比\(x\)小的元素停止
  3. 在该元素的位置上插入\(x\)
// 顺序表的存储结构定义如下(文件名seqlist.h)
#include 
#define N 100 // 预定义最大的数据域空间
typedef int datatype; // 假设数据类型为整型
typedef struct{
  datatype data[N]; // 此处假设数据元素只包含一个整型的关键字域
  int length; // 线性表长度
}seqlist; // 预定义的顺序表类型

// 算法insertx(L, x)用于有序顺序表插入结点并仍然有序
void insertx(seqlist *L, datatype x)
{
  int j;
  if (L->length < N)
  {
    j = L->length - 1;
    while (j >= 0 && L->data[j] > x)
    {
      L->data[j+1] = L->data[j]; // 元素统一往后挪
      j--;
    }
    L->data[j+1] = x;
    L->length++;
  }
}

你可能感兴趣的:(第2章 顺序表及其顺序存储)