栈与队列基础

139 阅读3分钟

栈与队列

225.用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

注意:

  • 你只能使用队列的基本操作 —— 也就是 push to backpeek/pop from frontsizeis empty 这些操作。
  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

解答思路

(1) 由于此题不涉及算法,所以我们只需要认真考虑两种数据结构的区别。

栈:后进先出。

队列:先进先出。

用队列模拟栈,首先我们得有一个队列。

225-2.svg

由于队列和栈的特性队列中最后一个元素就是栈顶元素,所以基于栈的操作用队列模拟,我们有两种做法

  • 依次出队,并将出队元素添加至队尾,直至最后一个元素到队头,一个对列即可实现
  • 出对,将出队数据保存到一个新的队列,直至旧的队列中只有一个元素。

由上面两种思路我们已经做到了队列模拟栈的基本逻辑。接下来我们来看代码。

// 第一种做法
var MyStack = function () {
    this.queue = []
};
​
/** 
 * @param {number} x
 * @return {void}
 */
MyStack.prototype.push = function (x) {
    this.queue.push(x)
};
​
/**
 * @return {number}
 */
MyStack.prototype.pop = function () {
   let size = this.queue.length
   // 当队列中只剩下队尾时,就是栈顶元素
   while (size-- > 1) {
        this.queue.push(this.queue.shift())
   }
   return this.queue.shift()
};
​
/**
 * @return {number}
 */
MyStack.prototype.top = function () {
    // 这个位置为了避免重复逻辑直接调用pop方法获得栈顶
   const x = this.pop()
   this.queue.push(x)
   return x
};
​
/**
 * @return {boolean}
 */
MyStack.prototype.empty = function () {
  return !this.queue.length
};
​
// 第二种做法
var MyStack = function () {
  this.queue1 = []
  this.queue2 = []
};
​
/** 
 * @param {number} x
 * @return {void}
 */
MyStack.prototype.push = function (x) {
  this.queue1.push(x)
};
​
/**
 * @return {number}
 */
MyStack.prototype.pop = function () {
  if (!this.queue1.length) {
    [this.queue1, this.queue2] = [this.queue2, this.queue1]
  }
  while (this.queue1.length > 1) {
    this.queue2.push(this.queue1.shift())
  }
​
  return this.queue1.shift()
};
​
/**
 * @return {number}
 */
MyStack.prototype.top = function () {
  const x = this.pop()
  this.queue1.push(x)
  return x

20.有效括号 ~ 1047

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

示例 4:

输入:s = "([)]"
输出:false

示例 5:

输入:s = "{[]}"
输出:true

思路:

(1)本题是一个匹配问题,匹配问题可以用栈来解决,我们先想一想什么情况是不匹配的。

(2)思考不匹配情况。

  • 遍历完发现栈不为空

  • 遍历到中间发现类型不匹配。

(3)抽象算法

  • 左括号进栈,右括号匹配栈。不匹配,return false.
  • 最后判断栈是否为空,为空true, 否则false

(4) 书写代码

方法1:

//这里匹配到左括号时进入右括号,是为了简便运算,我们只需要把括号理解为一个标记这个标记可以有由自己定。
var isValid = function (s) {
    const stack = []
    for (let i = 0; i < s.length; i++) {
        const c = s[i]
        switch (c) {
            case '[':
                stack.push(']')
                break
            case '{':
                stack.push('}')
                break
            case '(':
                stack.push(')')
                break
            default:
                if (stack.pop() !== c) {
                    return false
                }
        }
    }
    return stack.length === 0
};

方法2:

// 做一个映射标,可以很快的匹配
var isValid = function (s) {
    const map = {
        '{': '}',
        '[': ']',
        '(': ')'
    }
    const stack = []
​
    for (let i of s) {
        if (i in map) {
            stack.push(i)
            continue
        }
        if (map[stack.pop()] !== i) return false
    }
​
    return !stack.length
};

150.逆波兰表达式求值

根据 逆波兰表示法,求表达式的值。

有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

注意 两个整数之间的除法只保留整数部分。

可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

思路:

(1)首先我们要考虑后缀表达式的算法流程,因该是匹配到一个符号就要开始计算一个值,然后把这个值保存起来循环往复,这个情况可以使用栈结构,匹配到符号就出栈计算一次。

(2)考虑到有四个符号去匹配不同算法,我们可以使用键值对的方式进行对号入座,例如:”+“:(a, b) => a + b

(3)抽象算法。

  • 遍历tokens, 如果是符号就取出栈顶元素计算,并把计算的值进入栈,不是就该元素push进入栈。
  • 最后返回栈顶元素。

150.svg

(4)书写代码

var evalRPN = function(tokens) {
    const stack = []
    // 构造映射关系
    const map = new Map([
        ['+', (a, b) => a*1 + b*1],
        ['*', (a, b) => a*b],
        ['-', (a, b) => a - b],
        ['/', (a, b) => a / b]
    ])
​
    for (let i of tokens) {
        if (map.has(i)) {
            const a = stack.pop()
            const b = stack.pop()
            stack.push(parseInt(map.get(i)(b, a))) // 取整
        } else {
            stack.push(i)
        }
    }
    return stack.pop()
};