栈与队列理论知识
栈(Stack)
特点
- 后进先出(Last In First Out,LIFO)
- 只能在一端(栈顶)进行操作
基本操作
- push:将元素压入栈顶
- pop:移除并返回栈顶元素
- peek:查看栈顶元素但不移除
应用场景
- 函数调用栈
- 表达式求值(如中缀转后缀)
- 浏览器历史记录
代码实现
class Stack<T> {
private items: T[] = [];
push(element: T): void {
this.items.push(element);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
队列
特点
- 先进先出(First In First Out,FIFO)
- 一端(队尾)入队,另一端(队首)出队
基本操作
- enqueue:将元素添加到队列尾部
- dequeue:移除并返回队列头部的元素
- front:查看队列头部的元素但不移除
应用场景
- 任务队列(如JavaScript事件循环)
- 广度优先搜索(BFS)算法
- 打印机任务管理
代码实现
class Queue<T> {
private items: T[] = [];
enqueue(element: T): void {
this.items.push(element);
}
dequeue(): T | undefined {
return this.items.shift();
}
front(): T | undefined {
return this.items[0];
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
栈与队列的比较
| 特性 | 栈队 | 列 |
|---|---|---|
| 操作顺序 | 后进先出(LIFO) | 先进先出(FIFO) |
| 插入位置 | 栈顶 | 队尾 |
| 删除位置 | 栈顶 | 队首 |
| 主要操作 | push, pop | enqueue, dequeue |
| 辅助操作 | peek | front |
| 实现方式 | 数组或链表 | 数组、链表或循环数组 |
232.用栈实现队列
题目
使用栈实现队列的下列操作:
- push(x) -- 将一个元素放入队列的尾部。
- pop() -- 从队列首部移除元素。
- peek() -- 返回队列首部的元素。
- empty() -- 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
- 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
- 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
解题思路
使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入) ,再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。如果进栈和出栈都为空的话,说明模拟的队列为空了。 即:
- 使用两个栈:一个用于入队(stackIn),一个用于出队(stackOut)。
- push 操作:直接将元素压入 stackIn。
- pop 和 peek 操作:
- 如果 stackOut 为空,将 stackIn 中的所有元素依次弹出并压入 stackOut。
- 从 stackOut 中弹出(pop)或查看(peek)栈顶元素。
- empty 操作:当两个栈都为空时,队列为空。
代码实现
class MyQueue {
private stackIn: number[];
private stackOut: number[];
constructor() {
this.stackIn = [];
this.stackOut = [];
}
push(x: number): void {
this.stackIn.push(x);
}
pop(): number {
if (this.stackOut.length === 0) {
this.transfer();
}
return this.stackOut.pop()!;
}
peek(): number {
if (this.stackOut.length === 0) {
this.transfer();
}
return this.stackOut[this.stackOut.length - 1];
}
empty(): boolean {
return this.stackIn.length === 0 && this.stackOut.length === 0;
}
private transfer(): void {
while (this.stackIn.length > 0) {
this.stackOut.push(this.stackIn.pop()!);
}
}
}
225. 用队列实现栈
题目
使用队列实现栈的下列操作:
- push(x) -- 元素 x 入栈
- pop() -- 移除栈顶元素
- top() -- 获取栈顶元素
- empty() -- 返回栈是否为空
注意:
- 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
- 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
- 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
解题思路
- 使用一个队列来存储元素。
- push 操作:将新元素加入队列,然后将队列中除新元素外的所有元素依次出队并重新入队,这样新元素就位于队首,实现了栈的后进先出特性。
- pop 和 top 操作:直接操作队首元素。
- empty 操作:检查队列是否为空。
代码实现
class MyStack {
private queue: number[];
constructor() {
this.queue = [];
}
push(x: number): void {
this.queue.push(x);
for (let i = 1; i < this.queue.length; i++) {
this.queue.push(this.queue.shift()!);
}
}
pop(): number {
return this.queue.shift()!;
}
top(): number {
return this.queue[0];
}
empty(): boolean {
return this.queue.length === 0;
}
}
20. 有效的括号
题目
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
解题思路
- 该题目是字符串中每个字符需要与上一个字符进行对比,该关系想到使用栈结构,使用数组;
- 定义左右括号的匹配关系对照表,可以使用哈希表map(object)存储该信息
- 遍历字符串,如果遍历的字符是左括号,则放入栈中,否则从栈中取出一个元素与该右括号对应的左括号相比是否相等,如不相等则返回false;
- 当for循环结束后,检查数组是否为空,避免存在未完成匹配的左括号留在栈中,返回检查结果即可
代码实现
function isValid(s: string): boolean {
const mapTable = {
")": "(",
"}": "{",
"]": "[",
};
const stack: string[] = [];
for (let i = 0; i < s.length; i++) {
if (["(", "[", "{"].includes(s[i])) {
stack.push(s[i]);
} else {
if (mapTable[s[i]] !== stack.pop()) {
return false;
}
}
}
return stack.length === 0;
};
1047. 删除字符串中的所有相邻重复项
题目
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。 在 S 上反复执行重复项删除操作,直到无法继续删除。 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
- 输入:"abbaca"
- 输出:"ca"
- 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:
- 1 <= S.length <= 20000
- S 仅由小写英文字母组成。
解题思路
- 创建一个空栈。
- 遍历字符串中的每个字符:
- 如果栈为空或者当前字符与栈顶字符不同,将当前字符入栈。
- 如果当前字符与栈顶字符相同,将栈顶元素弹出(相当于删除了这对相邻重复项)。
- 栈中剩余的字符拼接并返回即可
代码实现
function removeDuplicates(s: string): string {
const stack: string[] = [];
for (let i = 0; i < s.length; i++) {
const str = s[i];
if (str !== stack[stack.length - 1]) {
stack.push(str)
} else {
stack.pop();
}
}
return stack.join('')
};