概念
栈又称为 "先进后出" 线性表,只有一端栈顶可以操作,另一端为固定端,称为栈底,
羽毛球盒
实际生活中,羽毛球盒,可以很形象的看成一个栈,只能在一端盒顶进行操作:
栈的常用方法:
push:添加一个元素到栈顶(相当于向羽毛球桶里放入一个羽毛球)
pop:弹出栈顶元素(从桶里拿出一个羽毛球)
top:返回(return)栈顶元素,不是出栈(看了一眼桶里最顶端的羽毛球)
isEmpty:判断栈是否为空(看下羽毛球是否用完了)
size:返回栈元素的个数。(数一桶里下羽毛球的个数)
clear:清空栈(把桶里羽毛球都倒出来扔掉)
数组实现栈:
function Stack() {
var mainStack = []
this.push = function(item) {
mainStack.push(item)
}
this.pop = function() {
return mainStack.pop()
}
this.top = function() {
return mainStack[mainStack.length - 1]
}
this.size = function() {
return mainStack.length
}
this.isEmpty = function() {
return mainStack.length === 0
}
this.clear = function() {
mainStack = []
}
}
练习题:
1、判断括号是否是对称的
思路:
(1)我们用栈的思想来解决此问题,首先遍历,碰到左括号直接入栈;
(2)如果碰到右括号,分两种情况,栈是否为空,如果不为空,则从栈顶弹出一个左括号(相当于左右括号抵消掉了);如果为空,说明右括号的数量多于左括号,直接返回false;
(3)遍历完,看下栈中是否有数据,有的话,说明左括号的数量多于右括号,返回false;栈为空,说明对称,返回true。
function isSMP(strParam) {
var stack = new Stack()
var len = strParam.length
for(var i = 0; i < len; i ++) {
if(strParam[i] === '(') {
stack.push(strParam[i])
}else if(strParam[i] === ')') {
if(stack.isEmpty()) {
return false
}else{
stack.pop()
}
}
}
return stack.isEmpty()
}
var res = isSMP('(FDS()FD)f')
console.log(res) // true
2、创建一个栈ReturnMinCountStack,除了常见的进栈、出栈的方法以外,写一个求栈最小值的方法,时间复杂度O1
思路:
(1)由于栈的特性"先进后出", 且只能操作栈顶元素,不可以遍历栈中的元素,所以此题需要在下边ReturnMinCountStack栈中,创建2个栈,一个栈存放压入栈的所有元素(存放每次入栈的元素),另一个栈存放最小值(这个栈是为求最小值的方法所准备的)。
(2)元素第一次入栈时,存放最小值的栈,一定是空的,且此元素一定是最小值,因为存放最小值的栈就这一个值;当下一次元素入栈时, 比较此元素是否小于存放最小值的栈的栈顶元素,如果小于,会把此元素压入最小值的栈中;否则,把存放最小值的栈的栈顶元素压入最小值栈中 去,这种方式会一直保持存放最小值的栈的栈顶元素是最小的。
function ReturnMinCountStack() {
var data_stack = new Stack()
var min_stack = new Stack()
this.push = function(item){
data_stack.push(item)
if(min_stack.isEmpty() || item < min_stack.top()) {
min_stack.push(item)
}else{
min_stack.push(min_stack.top())
}
}
this.pop = function() {
data_stack.pop()
min_stack.pop()
}
this.min = function() {
return min_stack.top() // 返回最小值的栈顶元素
}
}
var res = new ReturnMinCountStack()
res.push(1)
res.push(10)
res.push(0)
console.log(res.min()) // 0
3、后缀表达式(逆波兰式)运算
逆波兰式(或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后),之所以要把中序表达式转为后缀表达式(逆波兰式),因为逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
思路:遍历数组,数字直接入栈,如果是运算符,连续弹出2个元素,对元素进行运算,结果压入栈中,最后把计算结弹出。
// 逆波兰式 正常的运算是:6 + 4 / 2,转为后缀表达式就是下边参数形式
function endExpression(arr) {
var stack = new Stack()
arr.forEach(ele => {
if(['/', '-', '+', '*'].indexOf(ele) !== -1) {
var stckEle_1 = stack.pop()
var stackEle_2 = stack.pop()
var result = eval(stackEle_2 + ele + stckEle_1).toString()
stack.push(result)
}else{
stack.push(ele)
}
})
return stack.pop()
}
var res = endExpression([ "6", "4", "2", "/", "+" ])
console.log(res) // 8