先进后出(后进先出)

  • push 添加一个元素到栈顶
  • pop 弹出栈顶的元素
  • top 返回栈顶的元素
  • isEmpty 判断是否为空
  • size 返回栈里元素的个数
  • clear 清空栈

使用js的数组实现一个栈

  1. function Stack() {
  2. const items = []
  3. // 从栈顶添加元素,压栈
  4. this.push = function (item) {
  5. items.push(item)
  6. }
  7. // 弹出
  8. this.pop = function () {
  9. return !this.isEmpty() && items.pop()
  10. }
  11. // 返回栈顶元素
  12. this.top = function () {
  13. return !this.isEmpty() && items[items.length - 1]
  14. }
  15. // 判断是否为空
  16. this.isEmpty = function () {
  17. return items.length === 0
  18. }
  19. // 返回栈里的大小
  20. this.size = function () {
  21. return items.length
  22. }
  23. // 清空栈
  24. this.clear = function () {
  25. items.length = 0
  26. }
  27. }

判断括号是否合法

思路

遍历字符串,当遇到 ( 时,添加一个记号到栈中,表示这是第一对括号的开头。
当遇到 ) 时,我们从栈中取出一个记号,表示该括号为一组。
中途如果遇到 无法pop的时候,则表示缺少了 (。直接返回 false。
遍历结束后,我们判断栈的长度。如果栈空,则表示所有括号都抵消掉返回 true。如果长度不为 0,则表示其中缺少了 )。返回 false。

实现

  1. const str1 = '(12)(323)()123(1))'
  2. const str2 = '()(123123)'
  3. function isLegal(str) {
  4. const items = new Stack()
  5. for (let i = 0; i < str.length; i++) {
  6. if (str[i] === '(') {
  7. items.push(1)
  8. } else if (str[i] === ')') {
  9. if (items.isEmpty()) return false
  10. else items.pop()
  11. }
  12. }
  13. return items.isEmpty()
  14. }
  15. console.log(isLegal(str1), isLegal(str2))

计算逆波兰表达式(后缀运算)

思路

遍历该数组表达式,将所有数字添加到栈中。
当遇到运算符时,从栈中取出两个数字进行拼接,使用eval进行计算,并将结果重新压栈。继续循环
循环结束后,如果表达式正确会剩下最后一个数值,我们只需要从栈中取出来返回即可

实现

  1. // 4+13/5
  2. const exp1 = ['4', '13', '5', '/', '+']
  3. const exp2 = ['10', '6', '9', '3', '+', '-11', '*', '/', '*', '17', '+', '5', '+']
  4. function calcExp(exp) {
  5. const stack = new Stack()
  6. for (let i = 0; i < exp.length; i++) {
  7. const item = exp[i]
  8. if (['+', '-', '*', '/'].indexOf(item) >= 0) {
  9. const value1 = stack.pop()
  10. const value2 = stack.pop()
  11. const expStr = value2 + item + value1
  12. stack.push(parseInt(eval(expStr)).toString())
  13. } else {
  14. stack.push(item)
  15. }
  16. }
  17. return stack.pop()
  18. }
  19. console.log(calcExp(exp1))
  20. console.log(calcExp(exp2))

实现一个有min方法的栈

实现一个栈,除了常见的push,pop方法以外,提供一个min方法,返回栈里最小值。要求时间复杂度为 O(1)

思路

定义两个栈,一个正常存数据和操作,下文简称栈。另一个存每次的进栈时的最小值,下文简称min栈。
当每次push的时候,将值正常添加到栈中。我们判断min栈中,如果为空或者min栈顶的数据比添加的值大,那么我们进行压栈。此时min栈顶就是此次操作中的最小值。
每次pop的时候,栈和min栈正常操作即可。min栈pop一个元素后的栈顶。因为每次push我们都会往min栈中压入该状态的最小值。此时取栈顶的值,依旧是该栈中的最小值。

实现

  1. function MinStack() {
  2. const stack = new Stack()
  3. const minStack = new Stack()
  4. this.push = function (item) {
  5. stack.push(item)
  6. if (minStack.isEmpty() || minStack.top() > item) {
  7. minStack.push(item)
  8. } else {
  9. minStack.push(minStack.top())
  10. }
  11. }
  12. this.pop = function () {
  13. if (stack.isEmpty()) return false
  14. stack.pop()
  15. minStack.pop()
  16. }
  17. this.min = function () {
  18. return minStack.top()
  19. }
  20. }
  21. const minStack = new MinStack()
  22. minStack.push(3) // [3]
  23. console.log(minStack.min()) // 3
  24. minStack.push(2) // [3,2]
  25. console.log(minStack.min()) // 2
  26. minStack.push(5) // [3,2,5]
  27. console.log(minStack.min()) // 2
  28. minStack.push(-1) // [3,2,5,-1]
  29. console.log(minStack.min()) // -1
  30. minStack.pop() // [3,2,5]
  31. minStack.pop() // [3,2]
  32. minStack.pop() // [3]
  33. console.log(minStack.min()) // 3
  34. // 3
  35. // 2
  36. // 2
  37. // -1
  38. // 3

中序表达式转后缀表达式

思虑

定义一个栈和一个 list 数组。
循环表达式元素。
如果为数字,直接添加到 list 中。
如果为左括号,则压栈。
如果为右括号,则循环栈元素,并以此弹出栈顶元素到 list 中,直到遇到左括号结束循环。并弹出左括号。
如果为运算符,则判断当时的栈是否为空栈。如果空栈,则将运算符直接添加到空栈中。如果不为空栈则循环该栈,将栈顶的运算符优先级大于等于当前运算符的一次弹出,并添加到 list。直到找到优先级小于当前运算符为止。
将当前运算符压栈。
循环结束后,将栈中剩下的元素,依次弹出栈顶元素并添加到 list 中。
并返回 list 结果。

实现

  1. const priorityMap = {
  2. '+': 1,
  3. '-': 1,
  4. '*': 2,
  5. '/': 2
  6. }
  7. function infixExpToPostfixExp(exp) {
  8. const stack = new Stack()
  9. const list = []
  10. for (let i = 0; i < exp.length; i++) {
  11. const item = exp[i]
  12. if (!isNaN(item)) {
  13. list.push(item)
  14. } else if (item === '(') {
  15. stack.push(item)
  16. } else if (item === ')') {
  17. while (stack.top() !== '(') {
  18. list.push(stack.pop())
  19. }
  20. stack.pop()
  21. } else {
  22. while (!stack.isEmpty() && ['+', '-', '*', '/'].indexOf(stack.top()) !== -1 && priorityMap[stack.top()] >= priorityMap[item]) {
  23. list.push(stack.pop())
  24. }
  25. stack.push(item)
  26. }
  27. }
  28. while(!stack.isEmpty()) {
  29. list.push(stack.pop())
  30. }
  31. return list
  32. }
  33. const test1 = ['12', '+', '3'] // 12+3
  34. const test2 = ['2', '-', '3', '+', '2'] // 2-3+2
  35. const test3 = ['(', '1', '+', '(', '4', '+', '5', '+', '3', ')', '-', '3', ')', '+', '(', '9', '+', '8', ')'] // (1+(4+5+3)-3)+(9+8)
  36. console.log(infixExpToPostfixExp(test1))
  37. console.log(infixExpToPostfixExp(test2))
  38. console.log(infixExpToPostfixExp(test3))