js中的数据结构的栈
在 JavaScript 中,使用数组实现栈(Stack)是一种常见且高效的方法。栈是一种后进先出(LIFO, Last In First Out)的数据结构,这意味着最后添加的元素会是第一个被移除的元素。在许多应用场景中,如函数调用管理、表达式求值和撤销操作等,栈都扮演着重要角色。
栈的基础
栈(Stack)是一种特殊的线性数据结构,其特点是遵循“后进先出”(Last In, First Out,简称LIFO)的原则。这意味着最后一个进入栈的元素将是第一个被移除的元素。
栈的主要特性
-
后进先出(LIFO) :
- 在栈中,最后被添加的元素会最先被移除。例如,如果你依次将元素 A、B、C 压入栈中,那么在进行弹出操作时,首先弹出的是 C,其次是 B,最后是 A。
-
基本操作:
- Push:将一个元素添加到栈顶。
- Pop:移除并返回栈顶的元素。
- Peek:查看栈顶的元素,但不移除它。
- isEmpty:检查栈是否为空。
- Size:返回栈中元素的数量。
-
应用场景:
-
栈在许多算法和应用中都有广泛的应用,比如:
- 表达式求值(如逆波兰表达式)
- 括号匹配
- 深度优先搜索(DFS)
- 递归调用的管理
-
数组作为栈的优势
-
简洁性:JavaScript 的数组提供了内置的方法来方便地操作栈,例如 push() 和 pop()。
-
动态大小:数组的大小是动态的,可以根据需要自动扩展,而不需要手动管理内存。
-
易于实现:使用数组实现栈的逻辑简单,代码易于理解和维护。
简单示例
以下是一个完整的栈实现示例:
class Stack {
constructor() {
this.stack = [];
}
push(item) {
this.stack.push(item);
}
pop() {
return this.stack.pop();
}
peek() {
return this.stack[this.stack.length - 1];
}
isEmpty() {
return this.stack.length === 0;
}
size() {
return this.stack.length;
}
}
// 使用示例
let myStack = new Stack();
myStack.push(1);
myStack.push(2);
console.log(myStack.peek()); // 输出 2
console.log(myStack.pop()); // 输出 2
console.log(myStack.size()); // 输出 1
console.log(myStack.isEmpty()); // 输出 false
通过以上代码,你可以看到如何在 JavaScript 中实现栈的基本操作。
栈的操作图示
以下是栈操作的图示,展示了不同操作后的栈状态变化:
graph TD;
A[空栈] -->|push(1)| B[1]
B -->|push(2)| C[2\n1]
C -->|peek()| D[栈顶: 2]
C -->|pop()| E[1]
E -->|pop()| F[空栈]
图示说明
- 空栈:初始状态,栈为空。
- 压入 1:调用
myStack.push(1),栈中添加元素 1,状态为[1]。 - 压入 2:调用
myStack.push(2),栈中添加元素 2,状态为[2, 1](2 在上,1 在下)。 - 查看栈顶元素:调用
myStack.peek(),栈顶元素是 2,输出为2。 - 弹出元素:调用
myStack.pop(),栈顶元素 2 被移除,剩下元素 1,状态为[1],输出为2。 - 再次弹出元素:再调用
myStack.pop(),元素 1 被移除,栈变为空,状态为[]。
栈的算法
有效的括号
-
给定一个只包括
'(',')','{','}','[',']'的字符串s,判断字符串是否有效。 -
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
- 解决方案:
我们可以使用栈来解决这个问题。栈的 LIFO(后进先出)特性非常适合处理这种嵌套型的问题。
算法步骤:
-
创建一个空栈
stack。 -
遍历字符串
s中的每个字符:-
如果当前字符是左括号
'(','[','{',则将其对应的右括号')',']','}'推入栈中。 -
如果当前字符是右括号
')',']','}':- 检查栈是否为空,如果为空,则返回
false,因为没有左括号与之匹配。 - 否则,从栈顶弹出栈元素,检查是否与当前右括号匹配。如果不匹配,则返回
false。
- 检查栈是否为空,如果为空,则返回
-
-
如果遍历完字符串后栈为空,则说明所有括号都有匹配的左右括号,返回
true;否则返回false。
这样,通过栈的操作,我们可以有效地检查字符串中括号的闭合情况。
let s = '({[])'
var isValid = function (s) {
const obj = {
'(': ')',
'[': ']',
'{': '}'
}
//提前准备一个栈
//遍历字符串
//取到的是左括号,就把它的另一半存入栈中
//取到的是右括号,将栈顶的元素取出该字符进行对比
//如果栈为空,返回false
const stack = [];
for (let i = 0; i < s.length; i++) {
if (s[i] in obj) {
stack.push(obj[s[i]]);
} else {
if (stack.length == 0) {
return false;
}
if (s[i] != stack.pop()) {
return false;
}
}
};
return !stack.length
}
console.log(isValid(s))
在这个实现中,我们利用了 mapping 对象来映射左括号和右括号的对应关系,并使用栈来处理括号的匹配问题。遍历字符串 s,根据当前字符的类型进行相应的处理,最终判断栈是否为空来确定字符串是否有效。
每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
- 解决方案:
这个问题可以使用类似于上一个问题的栈方法来解决,主要利用栈来记录未找到更高温度的天数索引。
算法步骤:
-
创建一个空栈
stack和一个与temperatures长度相同的数组res,用于存放结果。 -
遍历
temperatures数组:-
如果栈不为空并且当前温度
temperatures[i]大于栈顶索引处的温度temperatures[stack[stack.length - 1]]:- 弹出栈顶元素
top,计算res[top]为当前索引i减去top的值,表示栈顶元素的下一个更高温度的等待天数。
- 弹出栈顶元素
-
将当前索引
i压入栈中,继续下一轮循环。
-
-
返回结果数组
res。
这样,通过栈的操作,可以高效地找到每天的下一个更高温度出现的等待天数。
let temperatures = [73,74,75,71,69,72,76,73]
var dailyTemperatures = function(temperatures) {
const len=temperatures.length;
const stack=[]
const res=new Array(len).fill(0)
for(let i=0;i<len;i++){
while(stack.length&&temperatures[i]>temperatures[stack[stack.length-1]]){
const top=stack.pop()
res[top]=i-top
}
stack.push(i)
}
return res
}
这个实现中,我们利用了栈来存储还未找到更高温度的天数索引,通过比较当前温度与栈顶元素的温度来确定下一个更高温度的等待天数,并更新结果数组 res。
小结
栈是一种非常重要的数据结构,具有广泛的应用。通过使用 JavaScript 的数组来实现栈,我们能够方便地进行各种操作。无论是在括号匹配问题还是在日常温度分析中,栈都能有效地帮助我们解决问题。