栈和队列
栈和队列
栈(stack)
- 栈是限定仅在表的一端进行插入或删除操作的线性表。
- 因此,对栈来说,可以进行插入删除操作的一端称为栈顶 (top), 相应地另一端称为栈底 (bottom)。
- 不含元素的空表称为空栈。
- 栈又称为后进先出 (Last In First Out, LIFO) 的线性表。
- 栈就像洗碗叠盘子取盘子一样。
栈的应用
- 浏览器前进后退
- 表达式计算
- 括号匹配检测
- 汉诺塔问题
- 迷宫求解问题
- 进制转换
- 逆序输出
栈的基本操作
| 操作 | 初始条件 | 操作结果 |
|---|---|---|
| InitStack(S) | 无 | 构造一个空栈S |
| DestroyStack(S) | 栈S已存在 | 栈S被销毁 |
| ClearStack(S) | 栈S已存在 | 将S清为空栈 |
| StackEmpty(S) | 栈S已存在 | 若栈 S 为空栈, 则返回 true, 否则返回 false |
| StackLength(S) | 栈S已存在 | 返回S的元素个数, 即栈的长度 |
| GetTop(S) | 栈S已存在且非空 | 返回S的栈顶元素, 不修改栈顶指针 |
| Push(S,e) | 栈S已存在 | 插入元素e为新的栈顶元素 |
| Pop(S,e) | 栈S已存在且非空 | 删除S的栈顶元素,并用e返回其值 |
| StackTraverse(S) | 栈S已存在且非空 | 从栈底到栈顶依次对S的每个数据元素进行访问 |
栈的两种实现
顺序存储结构实现
需要设置栈顶指针,数组末尾作为栈顶方便添加删除操作;
判空条件:
- 如果栈顶指针指向栈顶元素,栈顶指针 < 0 栈为空
- 如果栈顶指针指向栈顶元素上一位,栈顶指针 == 0 栈为空
缺点存储空间需要提前设定,动态扩容有一定代价;
链式存储结构实现
需要设置栈顶部指针,用链表表头作为栈顶方便添加删除操作;
不需要头指针,表头就是栈顶,链表为空 == 栈空== 表头为null;
利用栈将递归转为非递归
队列(queue)
- 队列是一种先进先出(First In First Out,FIFO)的线性表。
- 它只允许在表的一端进行插入,而在另一端删除元素。这和日常生活中的排队是一致的,最早进入队列的元素最早离开。
- 不含元素的空表称为空队列。
- 在队列中,允许插入的一端称为队尾(rear), 允许删除的一端则称为队头(front)。
队列的应用
-
排队系统
-
LRU cache
队列的基本操作
| 操作 | 初始条件 | 操作结果 |
|---|---|---|
| InitQueue(Q) | 无 | 构造一个空队列Q |
| DestroyQueue(Q) | 队列Q已存在 | 队列Q被销毁 |
| ClearQueue(Q) | 队列Q已存在 | 将Q清为空队列 |
| QueueEmpty(Q) | 队列Q已存在 | 若队列 Q 为空队列, 则返回 true, 否则返回 false |
| QueueLength(Q) | 队列Q已存在 | 返回Q的元素个数, 即队列的长度 |
| GetHead(Q) | 队列Q已存在且非空 | 返回Q的队头元素 |
| EnQueue(Q,e) | 队列Q已存在 | 插入元素e为新的队尾元素 |
| DeQueue(Q,e) | 队列Q已存在且非空 | 删除Q的队头元素,并用e返回其值 |
| QueueTraverse(Q) | 队列Q已存在且非空 | 从队头到队尾依次对Q的每个数据元素进行访问 |
队列的两种实现
顺序存储结构实现
顺序存储结构由于数组的特点在队尾进行插入为O(1),但是对头删除元素需要移动元素为O(n);
缺点存储空间需要提前设定,动态扩容有一定代价
循环队列可以用来解决数组的缺陷(出队需要移动大量元素|不移动出现假溢出):
循环队列需要头指针(指向第一个元素)、尾指针(指向最后一个元素下一位)
循环队列无法直接通过头指针==尾指针判断队列为空、已满
用额外标志位(入队加一,出队减一)
- front == rear 且 flag == 0 时对列为空;
- front == rear 且 flag != 0 时对列已满;
保留一位元素空间不使用
- front == rear 时对列为空;
- (rear+1)%capacity == front 时队列已满;
循环队列计算元素个数:(rear-front+capacity)%capacity == size
出队:front = (front+1)%capacity
入队:rear = (rear+1)%capacity
链式存储结构实现
通常采用单链表实现:
带有头结点
- 初始化时front = rear = head;
- 元素q入队:Q. rear->next = p;Q. rear=p;
- 队列判空:front == rear (== head 可省略)
- 获取对头元素:判断对列是否为空 非空返回Q. front->next;
- 元素出队:
- 判断对列非空
- p=Q. front->next;
- Q. front->next=p->next;
- 如果 Q. rear == p(最后一个元素出队) ; Q. rear = Q. front(队尾指向对头);
不带头结点
初始化时front == rear == null;
元素q入队:
- 空队列入队:Q. front=p(指向对头);Q. rear=p(指向队尾);
- 非空队列入队:Q. rear->next=p(连接新元素);Q. rear=p(rear指向队尾);
队列判空:front == rear == null
获取对头元素:判断对列是否为空 非空返回Q. front;
元素出队:
- 判断对列非空
- p = Q. front;
- Q. front = p->next;
- 如果 Q. rear == p(最后一个元素出队) ; Q. rear = Q. front(队尾指向对头);
总结:链表实现队列带不带头结点都可以,区别就是头结点是null还是占位节点

浙公网安备 33010602011771号