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

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

数据结构与算法_师大完整教程目录(更有python、go、pytorch、tensorflow、爬虫、人工智能教学等着你):https://www.cnblogs.com/nickchen121/p/13768298.html

一、线性表

  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. 栈的性质:后进先出(先进后出)
  5. 出栈元素不同排列个数:\(\frac{1}{n+1}C_{2n}^{n}\)

3.2 顺序栈及其实现

  1. 栈(顺序存储)的常用操作:

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

3.2.1 顺序栈的存储结构

#define MAXSIZE 100
typedef int datatype;
typedef struct {
    datatype a[MAXSIZE];
    int top;
} sequence_stack;

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

4.2.1 顺序队列的存储结构

#define MAXSIZE 100
typedef int datatype;
typedef struct {
    datatype a[MAXSIZE];
    int front;
    int rear;
} sequence_queue;

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. 循环队列的删除操作

4.3.1 顺序循环队列的存储结构

#define MAXSIZE 100
typedef int datatype;
typedef struct {
    datatype a[MAXSIZE];
    int front;
    int rear;
} sequence_queue;

五、算法设计题

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

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

算法步骤:

  1. 设定初始值\(c\)计数
  2. 每找到一个值为\(x\)的结点,计数加\(1\)
  3. 返回\(c\)
// 顺序表的存储结构定义如下(文件名seqlist.h)
#include <stdio.h>

#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 <stdio.h>

#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 <stdio.h>

#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++;
    }
}

六、错题集

  1. 长为 \(n\) 的顺序表中,任意位置插入的可能次数为n+1​次(包括头前和尾后)

  2. 设栈 \(S\) 和队列 \(Q\) 的初始状态为空,元素 e1、$e\(2、\)e\(3、\)e\(4、\)e$5 和 $e$6 依次通过栈 \(S\)
    一个元素出栈后即进入队列 \(Q\),若 6 个元素出队的序列为 $e\(2、\)e\(4、\)e\(3、\)e\(6、\)e$5 和 $e$1,则栈 \(S\)
    的容量至少应该为3

  3. 编号为 \(1,2,3,4\) 的四列火车通过一个栈式的列车调度站,可能得到的调度结果总共有14种

    1. 使用公式:出栈元素不同排列个数为 \(\frac{1}{n+1}C_{2n}^{n}\)
posted @ 2020-10-03 21:42  B站-水论文的程序猿  阅读(1686)  评论(0编辑  收藏  举报