数据结构系列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 - 删除可能导致队空,每次删除时置
tag为0,插入时可能导致队满,每次插入时置tag为1
- 用
表示
- 顺序存储
- 队空条件:
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.frontQ.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;
}
栈和队列的应用
表达式求值
- 中缀表达式化后缀表达式
- 数字直接输出进入队列
- 有括号,左括号直接入(不比较优先级),右括号匹配左括号,中间的全部出进入队列
- 新入的操作符下方的必须低于其(括号优先级最低),大于等于进入队列
- 后缀表达式求值
- 数字进栈
- 操作符从栈中依次弹出
op2和op1,计算值并入栈 - 最后栈顶元素即为结果
递归
- 递归执行过程
// 斐波那契数列
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n - 1) + fib(n - 2);
}

矩阵压缩存储
二维数组的存储结构
设二位数组的行下标范围为$[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]\)中,元素下标的对应关系如下(只存放下三角部分)

\[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存储上方的常量,存在最后),元素下标间的对应关系如下

\[k = \begin{cases}
\frac{i(i - 1)}{2} + j - 1, & i \ge j(下三角区和主对角线元素) \\
\\
\frac{n(n - 1)}{2}, & i < j(上三角区元素)
\end{cases}
\]
- 上三角矩阵,下三角区的所有元素均为同一常量,类似下三角区

\[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]中

\[\begin{cases}
k = 2i + j - 3 \\
i = \lfloor (k + 1) / 3 + 1 \rfloor \\
j = k - 2i + 3
\end{cases}
\]
- 稀疏矩阵,构成三元组(行标,列标,值),可以使用数组或十字链表法存储,存储后失去随机存取特性

浙公网安备 33010602011771号