Loading

数据结构系列2——栈和队列

栈和队列

概念

  • 栈是只允许在一端进行插入或删除操作的线性表
  • 栈的操作特性:后进先出(LIFO)
  • 共享栈将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸

表示

  • 顺序栈
    • 初始时S.top = -1
    • 判断栈空条件S.top == -1,判断栈满条件S.top == MAXSIZE - 1
const int MAXSIZE = 50;
struct SqStack {
    int data[MAXSIZE], top;
};
  • 链栈
struct LiStack {
	int data;
	LiStack* next;
}

基本操作

  • 初始化
void initStack(SqStack &S) {
    S.top = -1;
}
  • 判栈空
bool stackEmpty(SqStack S) {
    return S.top == -1;
}
  • 进栈
bool push(SqStack &S, int val) {
    if (S.top == MAXSIZE - 1) return false;
    S.data[++S.top] = val;
    return true;
}
  • 出栈
bool pop(SqStack &S, int &val) {
    if (S.top == -1) return false;
    val = S.data[S.top--];
    return true;
}
  • 读栈顶元素
bool getTop(SqStack S, int &val) {
    if (S.top == -1) return false;
    val = S.data[S.top];
    return true;
}

队列

概念

  • 只允许在表的一端插入,另一端删除的线性表
  • 队列的操作特点:先进先出(FIFO)
  • 循环队列
    • 表示与顺序队列相同,入队时指针按顺时针(下标增大方向)进一
    • 队列长度:(Q.rear + MAXSIZE - Q.front) % MAXSIZE
    • 区分队空和队满——牺牲单元
      • 队满条件:(Q.rear + 1) % MAXSIZE == Q.front
      • 队空条件:Q.front == Q.rear
      • 队列中元素个数:(Q.rear + MAXSIZE - Q.front) % MAXSIZE
    • 区分队空和队满——设置元素个数
      • 队空:Q.size == 0
      • 队满:Q.size == MAXSIZE
    • 区分队空和队满——设置tag
      • tag表示队满还是队空
      • 队空:Q.rear == Q.front && Q.tag == 0
      • 队满:Q.rear == Q.front && Q.tag == 1
      • 删除可能导致队空,每次删除时置tag0,插入时可能导致队满,每次插入时置tag1

表示

  • 顺序存储
    • 队空条件:Q.front == Q.rear && Q.front == 0
    • 不能用Q.rear == MAXSIZE作为队满条件,注意假溢出
const int MAXSIZE = 50;
struct SqQueue {
    int data[MAXSIZE];
    int front, rear;
};
  • 链式存储(带头结点的队列)
    • Q.front始终指向头节点(不存储数据),若队列为空,Q.rear也指向头节点,因此得队列空的判断条件:Q.rear == Q.front
    • Q.rear指向队尾节点,而非下一个位置
    • 注意入队和出队涉及到最后一个元素的修改时,头指针和尾指针都要修改
struct node {
    int val;
    node* next;
};

struct LinkQueue {
    node *front, *rear;
};

基本操作(循环队列)

  • 初始化
void initQueue(SqQueue &Q) {
    Q.front = Q.rear = 0;
}
  • 判队空
bool isEmpty(SqQueue Q) {
    return Q.rear == Q.front;
}
  • 入队
bool enQueue(SqQueue &Q, int val) {
    if ((Q.rear + 1) % MAXSIZE == Q.front) return false;
    Q.data[Q.rear] = val;
    Q.rear = (Q.rear + 1) % MAXSIZE;
    return true;
}
  • 出队
bool deQueue(SqQueue &Q, int &val) {
    if (isEmpty(Q)) return false;
    val = Q.data[Q.front];
    Q.front = (Q.front + 1) % MAXSIZE;
    return true;
}

基本操作(链式队列)

  • 初始化
void initQueue(LinkQueue &Q) {
    Q.front = Q.rear = new node();
    Q.front->next = nullptr;
}
  • 判队空
bool isEmpty(LinkQueue Q) {
    return Q.rear == Q.front;
}
  • 入队
// 链式存储可分配空间,不判断是否队满
void enQueue(LinkQueue &Q, int val) {
    node* cur = new node(val);
    Q.rear->next = cur;
    //修改了Q.rear
    Q.rear = cur;
}
  • 出队
bool deQueue(LinkQueue &Q, int &val) {
    if (isEmpty(Q)) return false;
    node* p = Q.front->next;
    val = p->val;
    // 队头指针一直指向头节点,入队与出队操作均不改变
    Q.front->next = p->next;
    // p为最后一个(队尾)元素,出队后队空
    if (Q.rear = p) Q.rear = Q.front;
    // free后p仍指向原来的地址,只不过原来的地址存放的内容被释放,现在该地址的内容无意义
    free(p);
    return true;
}

栈和队列的应用

表达式求值

  • 中缀表达式化后缀表达式
    • 数字直接输出进入队列
    • 有括号,左括号直接入(不比较优先级),右括号匹配左括号,中间的全部出进入队列
    • 新入的操作符下方的必须低于其(括号优先级最低),大于等于进入队列
  • 后缀表达式求值
    • 数字进栈
    • 操作符从栈中依次弹出op2op1,计算值并入栈
    • 最后栈顶元素即为结果

递归

  • 递归执行过程
// 斐波那契数列
int fib(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;
    return fib(n - 1) + fib(n - 2);
}

20220717113738

矩阵压缩存储

二维数组的存储结构

设二位数组的行下标范围为$[0, h_{1}] \(和\)[0, h_{2}]$

  • 按行存储

\[LOC(a_{i, j}) = LOC (a_{0, 0}) + [i \times (h_{2} + 1) + j] \times L \]

  • 按列存储

\[LOC(a_{i, j}) = LOC(a_{0, 0}) + [j \times (h_{1} + 1) + i] \times L \]

矩阵的压缩存储

  • 对称矩阵,将对称矩阵\(A[1 \ldots n][1 \ldots n]\)存放在一维数组\(B[n(n + 1) / 2]\)中,元素下标的对应关系如下(只存放下三角部分
    20220717113804

\[k = \begin{cases} \frac{i(i - 1)}{2} + j - 1, & i \ge j(下三角区和主对角线元素) \\ \\ \frac{j(j - 1)}{2} + i - 1, & i < j(上三角区元素,a_{ij} = a_{ji}) \\ \end{cases} \]

  • 下三角矩阵,上三角所有元素均为同意常量,将下三角矩阵\(A[1 \ldots n][1 \ldots n]\)压缩存储在\(B[n(n + 1) / 2 + 1]\)中(多出来的1存储上方的常量,存在最后),元素下标间的对应关系如下
    20220717113832

\[k = \begin{cases} \frac{i(i - 1)}{2} + j - 1, & i \ge j(下三角区和主对角线元素) \\ \\ \frac{n(n - 1)}{2}, & i < j(上三角区元素) \end{cases} \]

  • 上三角矩阵,下三角区的所有元素均为同一常量,类似下三角区
    20220717113845

\[k = \begin{cases} \frac{(i - 1)(2n - i + 2)}{2} + (j - 1), & i \le j(上三角区和主对角线元素)\\ \\ \frac{n(n + 1)}{2}, & i > j(下三角区元素) \end{cases} \]

  • 三对角矩阵,对于n阶方阵A中的任一元素\(a_{i,j}\),当\(\lvert i - j \rvert > 1\)时,有\(a_{i,j} = 0 (1 \le i, j \le n)\),则称为三对角矩阵,以行优先方式存放在一维数组B中,\(a_{1,1}\)存放于B[0]中
    20220717113915

\[\begin{cases} k = 2i + j - 3 \\ i = \lfloor (k + 1) / 3 + 1 \rfloor \\ j = k - 2i + 3 \end{cases} \]

  • 稀疏矩阵,构成三元组(行标,列标,值),可以使用数组或十字链表法存储,存储后失去随机存取特性
posted @ 2022-07-17 11:40  Patrickhao  阅读(15)  评论(0)    收藏  举报