文章目录
栈与队列都是受限的线性表,其 逻辑结构相同,均为——线性表。
顺序存储 | 链式存储 | |
---|---|---|
栈 | 顺序栈 | 链栈 |
队列 | 循环队列 | 链队列 |
主要考顺序存储,并且栈/队列单独只出现在选择题,在考研大题中都是作为一个工具来解决其他问题的,可以直接用数组表示,而不需要写各种函数。
一.栈 (stack)
栈顶(Top) | 进行插入/删除的一端。(只需定义栈顶即可) |
栈底(Bottom) | 固定的一端,不进行操作。 |
空栈 | 没有元素的空表。 |
操作特性 | 后进先出(LIFO) |
n个不同的元素顺序进栈,其出栈的不同排列的个数:
1
n
+
1
C
2
n
n
\color{cyan}\frac{1}{n+1}C_{2n}^{\ n}
n+11C2n n
1.顺序栈 (SqStack)
//简单定义加初始化
int data[Maxsize];
int top=-1;
格式化书写:
typedef struct{
int data[Maxsize];
int top;
}SqStack;
注:top指向栈顶元素
初始时,S.top = -1;
栈顶元素:S.data[S.top]
入栈:S.data[++S.top] = x;
,出栈:x = S.data[S.top--];
栈空:S.top == -1
,栈满:S.top == Maxsize-1
共享栈:
int data[Maxsize];
int top1 = -1 , top2 = Maxsize;
更有效地利用空间。
2.链栈 (LiSack)
带头结点/不带头结点均可。
bool push(LiStack &S); 入栈
bool pop(LiStack &S,int &x)
链头入栈:头插法
如果写函数,入栈/出栈记得传引用!
链栈出栈:判断是否为空
统考真题:
栈的统考真题都时考栈的顺序进栈,的出栈情况
一般情况下手写遍历一下就可以, 1 n + 1 C 2 n n \frac{1}{n+1}C_{2n}^n n+11C2nn一般用不上
递归 → \rightarrow →非递归,不一定要栈,比如斐波那契数列直接循环就行了
二.队列 (Queue)
队头(Front) | 进行删除的一端 |
队尾(Rear) | 进行插入的一端 |
空队 | 没有元素的空表 |
操作特性 | 先进先出FIFO |
1.循环队列
单纯的顺序队列,有时虽然rear已经到数组尾了,但是因为队头front的不断前进,导致后面有很多空间空了出来不能被利用,这就导致了假溢出。
为了充分利用数组空间,我们使用循环队列。
front:指向队头元素
rear:指向队尾元素的下一个位置
初始时:Q.front = Q.rear = 0;
入队:Q.rear = (Q.rear + 1) % Maxsize;
出队:Q.front = (Q.front + 1) % Maxsize;
为了区分队空/队满,有以下三种常用方法:(主要第一个)
1.牺牲一个单元,Q.rear 所指的块永远为空
队空:Q.front == Q.rear
队满:(Q.rear + 1)%Maxsize == Q.front
元素个数:(Q.rear-Q.front+Maxsize)%Maxsize;
2.加一个表示元素个数的数据成员 Q.size
队空:Q.size == 0
队满:Q.size == Maxsize
3.加一个 tag 数据成员,区分队空、队满
入队:Q.tag = 1
出队:Q.tag = 0
Q.front == Q.rear
时,若Q.tag == 0
,则队空。若Q.tag == 1
,则队满。
2.链队列
就是同时带头指针 front 和 尾指针 rear 的单链表
typedef struct LinkNode{
int data;
LinkNode* next; //结点
}LinkNode;
typedef struct{
LinkNode *front,*rear; //仅保存头尾指针
}LinkQueue;
假设带头结点:
队空:Q.front == Q.rear
出队:判断是否为空、最后一个结点
bool Dequeue(Linkqueue& Q, int &x) {
if (Q.rear == Q.front)
return false;
Linknode* p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if (p == Q.rear) //如果就一个结点,rear指向front的结点,置空
Q.rear = Q.front;
delete(p);
return true;
}
3.双端队列
输入受限的双端队列
输出受限的双端队列
统考真题:
大多是顺序存储的,循环队列,双端队列,19年考了一个链式队列
三.栈和队列的应用
栈 | 括号匹配 表达式求值(后缀表达式) 递归 迷宫求解 |
队列 | 层次遍历 缓冲区 页面替换算法 广度优先搜索图 |
1.括号匹配:
(1)左括号压栈,遇到右括号即取栈顶判断,若匹配则继续,若不匹配则匹配失败。
(2)若括号全部计算完后,栈内有剩余,则匹配失败。
2.表达式求值:
先用运算符栈,由中缀表达式得到后缀表达式,然后用数字栈得到计算结果。
中缀表达式 → \rightarrow → 后缀表达式 (符号栈)
1.手写法
将所有操作用括号括起来,然后将操作符挪到括号后面,然后去掉括号。
a*(b+c)-d
→
\rightarrow
→ ((a*(b+c))-d)
→
\rightarrow
→ ((a(bc)+)*d)-
→
\rightarrow
→ abc+*d-
2.栈方法:
- 运算数,直接输出。
- 运算符(括号除外),压栈时判断,将栈内优先级 ≥ \ge ≥ 自己的全部弹出输出(有就左括号停止)。
- 对于左右括号 () ,左括号直接压栈。遇到右括号,向下一直弹栈,直到弹出左括号为止。括号不输出。
- 若操作数没有了,则全部弹栈
a*(b+c)-d
输出 | 运算符栈 | |
---|---|---|
a | ||
a | * | * 压栈 |
a | ( * | ( 压栈 |
ab | + ( * | + 压栈 |
abc | + ( * | 输出c |
abc+ | * | ) 出现,弹栈,直到弹出 ( |
abc+* | - | * ≥ \ge ≥ -,* 弹栈 |
abc+*d | - | d输出 |
abc+*d- | - | 弹栈 |
后缀表达式 → \rightarrow → 结果 (数字栈)
从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号(op),就将处于栈顶两个数字出栈(a,b),进行运算(b op a),运算结果进栈,一直到最终获得结果。
a*(b+c)-d
abc+*d-
比如 123+*4- = 1*(2+3)-4 = 1
操作数栈 | 符号 | |
---|---|---|
3 2 1 | 数字压栈 | |
1 | 2 + 3 | +,弹出3,2,计算2+3 |
5 1 | 得到结果5,压栈 | |
1 * 5 | *,弹出5,1,计算1*5 | |
5 | 得到结果5,压栈 | |
4 5 | 数字压栈 | |
5 - 4 | -,弹出4,5,计算5-4 | |
1 | 结果 |
3.递归
(1).递归调用时,系统会为每一层递归函数的返回点,局部变量,传入参数等开辟递归工作栈来存储数据。
(2).递归次数过多容易造成栈溢出。
(3).递归的优点是可以用很精简的代码解决问题,但是递归效率低,因为包含了很多的重复计算。(分治法一般都是)
(4).递归的应用:
阶乘问题、二叉树深度、汉诺塔问题、斐波那契数列、快速排序、归并排序(分治算法体现递归)、遍历文件,解析xml文件
(5).解决递归问题一般就三步曲,分别是:
第一步,定义函数功能
第二步,寻找递归终止条件
第二步,递推函数的等价关系式
4.二叉树层次遍历:
(1).根入队
(2).若队空,结束遍历
(3).队中第一个结点出队,若其有左孩子,左孩子入队,若有右孩子,右孩子入队。
统考真题:
中缀转后缀表达式的栈方法,一定要掌握!考了两次
函数的栈存储考了一次
缓冲区队列一次
内容不多…
四.特殊矩阵的压缩存储
注:矩阵是[1…n][1…n],首先看按行/列优先存取,其次存到一维数组B[0…k-1],其坐标从0开始!所以计算后要 -1!
1.对称矩阵
a
i
j
=
a
j
i
a_{ij}=a_{ji}
aij=aji,我们只需要存储:上/下三角+对角线即可
A
[
1..
n
]
[
1..
n
]
→
B
[
n
(
n
+
1
)
2
]
A[1..n][1..n]\rightarrow B[\frac{n(n+1)}{2}]
A[1..n][1..n]→B[2n(n+1)],若存储下三角矩阵,按行存储,则
a
i
j
=
(
1
+
2
+
.
.
.
+
i
−
1
+
j
)
−
1
=
B
[
i
(
i
−
1
)
2
+
j
−
1
]
a_{ij}=(1+2+...+i-1+j)-1=B[\frac{i(i-1)}{2}+j-1]
aij=(1+2+...+i−1+j)−1=B[2i(i−1)+j−1]上三角的元素在B中为:
B
[
j
(
j
−
1
)
2
+
i
−
1
]
B[\frac{j(j-1)}{2}+i-1]
B[2j(j−1)+i−1]
2.三角矩阵
即上/下三角部分为常数,我们只需要存储非常数的三角+对角线+常数
A
[
1..
n
]
[
1..
n
]
→
B
[
n
(
n
+
1
)
2
+
1
]
A[1..n][1..n]\rightarrow B[\frac{n(n+1)}{2}+1]
A[1..n][1..n]→B[2n(n+1)+1]
3.三对角矩阵
三对角矩阵,即只有以主对角线为中心的三个对角线上元素非0,其他地方元素都是0。
第一行,第n行都只有2个元素,其他行都有3个元素,
a
i
i
a_{ii}
aii为第i行第2个。
a
i
j
=
2
+
3
∗
(
i
−
2
)
+
(
j
−
i
+
2
)
−
1
=
B
[
2
i
+
j
−
3
]
a_{ij}=2+3*(i-2)+(j-i+2)-1=B[2i+j-3]
aij=2+3∗(i−2)+(j−i+2)−1=B[2i+j−3]
4.稀疏矩阵
非0元极少,大多数都是0
我们可以只存储非0元的 行、列、数值
三元组、十字链表法均可以完成。
统考真题,统一格式:
1.给定一个特殊矩阵
2.说明按行/列,存入一维数组
3. a i j a_{ij} aij在数组中的下标!
坑点:
看清楚按行/列存储,20年突然写个按列存储…
求的是下标!矩阵都是(1,1)开始,所以最后要记得 − 1 -1 −1
考过了,对称矩阵,三对角矩阵,稀疏矩阵,不知道下一次会考什么矩阵…