LeetCode Cookbook 栈和队列 上篇

LeetCode Cookbook 栈和队列 上篇

   栈和队列的习题,上篇主要给出三个类型的题目:(1)model-I 主要是括号匹配、检验、删减类型的题目;(2)model-II则是一些有关栈、队列的数据结构型的题目;(3)model-III 是一些等式计算型题目。这三种类型的题目都非常的经典,且难度适宜,可以多刷几遍巩固对栈和队列结构的认识和应用。

20. 有效的括号(model-I 括号 )

题目链接:20. 有效的括号
题目大意:给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

例如:

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

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

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

解题思路:哈希表储存符合要求的 括号对。

class Solution:
    def isValid(self, s: str) -> bool:
        dic = {'(':')','[':']','{':'}','.':'.'}
        stack = ['.']
        for ch in s:
            if ch in dic: stack.append(ch)
            elif dic[stack.pop()] != ch: return False
        return len(stack) == 1

856. 括号的分数(model-I 扩展1 加动态规划)

题目链接:856. 括号的分数
题目大意:给定一个平衡括号字符串 S,按下述规则计算该字符串的分数:

  • () 得 1 分。
  • AB 得 A + B 分,其中 A 和 B 是平衡括号字符串。
  • (A) 得 2 * A 分,其中 A 是平衡括号字符串。

例如:

输入: "()"
输出: 1

输入: "(())"
输出: 2

解题思路: 非常有趣的一道题,栈结构解析括号的内外层,动态规划则用以计算得分。

class Solution:
    def scoreOfParentheses(self, s: str) -> int:
        stack = list([0])
        for ch in s:
            if ch == '(':
                stack.append(0)
            else:
                num = stack.pop()
                # 这个动态规划 属实不要不要的
                stack[-1] += max(2*num,1)
        return stack.pop()

921. 使括号有效的最少添加(model-I 扩展2)

题目链接: 921. 使括号有效的最少添加表
题目大意:只有满足下面几点之一,括号字符串才是有效的:

  • 它是一个空字符串,或者
  • 它可以被写成 AB (A 与 B 连接), 其中 A 和 B 都是有效字符串,或者
  • 它可以被写作 (A),其中 A 是有效字符串。
    给定一个括号字符串 s ,移动N次,你就可以在字符串的任何位置插入一个括号。
  • 例如,如果 s = “()))” ,你可以插入一个开始括号为 “(()))” 或结束括号为 “())))” 。
    返回 为使结果字符串 s 有效而必须添加的最少括号数

例如:

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

输入:s = "((("
输出:3

解题思路:利用栈的特性 吐出 符合规则的括号。

class Solution:
    def minAddToMakeValid(self, s: str) -> int:
        stack = list()
        for ch in s:
            if ch == '(': stack.append(ch)
            elif ch == ')' and stack and stack[-1]=='(':
                stack.pop()
            else: stack.append(ch)
        return len(stack)

1021. 删除最外层的括号(model-I 扩展3)

题目链接:1021. 删除最外层的括号表
题目大意:有效括号字符串为空 “”、“(” + A + “)” 或 A + B ,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。

  • 例如,“”,“()”,“(())()” 和 “(()(()))” 都是有效的括号字符串。
    如果有效字符串 s 非空,且不存在将其拆分为 s = A + B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。
  • 给出一个非空有效字符串 s,考虑将其进行原语化分解,使得:s = P_1 + P_2 + … + P_k,其中 P_i 是有效括号字符串原语。
  • 对 s 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 s 。

例如:

输入:s = "(()())(())"
输出:"()()()"
解释:
输入字符串为 "(()())(())",原语化分解得到 "(()())" + "(())",
删除每个部分中的最外层括号后得到 "()()" + "()" = "()()()"

解题思路:与 921. 使括号有效的最少添加表 有异曲同工之妙,不过这里 stack 的用途则是控制 ans 中的括号数量。

class Solution:
    def removeOuterParentheses(self, s: str) -> str:
        ans,stack = "",list()
        for ch in s:
            if ch == ')':
                stack.pop()
            if stack:
                ans += ch
            if ch == '(':
                stack.append(ch)
        return ans

71. 简化路径(model-I 扩展4 加字符串)

题目链接:71. 简化路径
题目大意:给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 ‘/’ 开头),请你将其转化为更加简洁的规范路径。在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,‘//’)都被视为单个斜杠 ‘/’ 。 对于此问题,任何其他格式的点(例如,‘…’)均被视为文件/目录名称。
请注意,返回的 规范路径 必须遵循下述格式:

  • 始终以斜杠 ‘/’ 开头。
  • 两个目录名之间必须只有一个斜杠 ‘/’ 。
  • 最后一个目录名(如果存在)不能 以 ‘/’ 结尾。
  • 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 ‘.’ 或 ‘…’)。
    返回简化后得到的 规范路径 。

例如:

输入:path = "/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。 

输入:path = "/a/./b/../../c/"
输出:"/c"

解题思路: 不难 有点像字符串的题目 但结合栈特性 更好、更快的处理。

class Solution:
    def simplifyPath(self, path: str) -> str:
        file = path.split('/')
        stack = []
        for ch in file:
            if ch == '..':
                if stack: 
                    stack.pop()
            elif ch and ch != '.':
                stack.append(ch)
        return '/'+"/".join(stack)

155. 最小栈(model-II 最小栈)

题目链接:155. 最小栈
题目大意:设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。

  • void push(int val) 将元素val推入堆栈。

  • void pop() 删除堆栈顶部的元素。

  • int top() 获取堆栈顶部的元素。

  • int getMin() 获取堆栈中的最小元素。

    例如:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

解题思路: 双链表解决最小栈问题。

class MinStack:
    
    # build two stacks realize this goal
    def __init__(self):
        self.stack = list()
        self.minStack = [math.inf]

    def push(self, val: int) -> None:
        # The stack and minStack have same elements
        self.stack.append(val)
        self.minStack.append(min(val,self.minStack[-1]))

    def pop(self) -> None:
        self.stack.pop()
        self.minStack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        return self.minStack[-1]

# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

225. 用队列实现栈(model-II 队列->栈)

题目链接:225. 用队列实现栈
题目大意:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:

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

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

例如:

输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

解题思路:双队列 实现 栈入栈出。

class MyStack:

    def __init__(self):
        # 主要是通过两个队列将数组的顺序进行翻转
        self.q1 = collections.deque()
        self.q2 = collections.deque()

    def push(self, x: int) -> None:
        self.q2.append(x)
        while self.q1:
            self.q2.append(self.q1.popleft())
        self.q1,self.q2 = self.q2,self.q1

    def pop(self) -> int:
        return self.q1.popleft()

    def top(self) -> int:
        return self.q1[0]

    def empty(self) -> bool:
        return not self.q1

# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

232. 用栈实现队列(model-II 栈->队列)

题目链接:232. 用栈实现队列
题目大意:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty),实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false
    说明:你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

例如:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

解题思路:双栈 加上 一个旗帜 更快一些。

class MyQueue:

    def __init__(self):
        # The task of stack1 is collecting numbers
        # And the mission of stack2 is simulating queue。
        self.stack1 = list()
        self.stack2 = list()
        # 设置一个旗杆专门用来处理 只有一个元素的入队与出队
        self.flag = None

    def push(self, x: int) -> None:
        if not self.stack1: self.flag = x 
        self.stack1.append(x)

    def pop(self) -> int:
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            # remember cancel self.flag
            self.flag = None
        return self.stack2.pop()

    def peek(self) -> int:
        if self.stack2:
            return self.stack2[-1]
        return self.flag

    def empty(self) -> bool:
        return len(self.stack1)==0 and len(self.stack2)==0

# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

895. 最大频率栈(model-II 双栈->频率栈)

题目链接:895. 最大频率栈
题目大意:设计一个类似堆栈的数据结构,将元素推入堆栈,并从堆栈中弹出出现频率最高的元素。
实现 FreqStack 类:

  • FreqStack() 构造一个空的堆栈。
  • void push(int val) 将一个整数 val 压入栈顶。
  • int pop() 删除并返回堆栈中出现频率最高的元素。如果出现频率最高的元素不只一个,则移除并返回最接近栈顶的元素。

例如:

输入:
["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"],
[[],[5],[7],[5],[7],[4],[5],[],[],[],[]]
输出:[null,null,null,null,null,null,null,5,7,5,4]
解释:
FreqStack = new FreqStack();
freqStack.push (5);//堆栈为 [5]
freqStack.push (7);//堆栈是 [5,7]
freqStack.push (5);//堆栈是 [5,7,5]
freqStack.push (7);//堆栈是 [5,7,5,7]
freqStack.push (4);//堆栈是 [5,7,5,7,4]
freqStack.push (5);//堆栈是 [5,7,5,7,4,5]
freqStack.pop ();//返回 5 ,因为 5 出现频率最高。堆栈变成 [5,7,5,7,4]。
freqStack.pop ();//返回 7 ,因为 57 出现频率最高,但7最接近顶部。堆栈变成 [5,7,5,4]。
freqStack.pop ();//返回 5 ,因为 5 出现频率最高。堆栈变成 [5,7,4]。
freqStack.pop ();//返回 4 ,因为 4, 57 出现频率最高,但 4 是最接近顶部的。堆栈变成 [5,7]

解题思路: 设置三个变量 分别用来: (1)计频;(2)记元素;(3)固定最大频次。

class FreqStack:

    def __init__(self):
        
        self.counter = collections.Counter()
        self.hash_map = collections. defaultdict(list)
        self.max_freq = 0

    def push(self, x: int) -> None:
        f = self.counter[x]+1
        self.counter[x] = f
        if f > self.max_freq:
            self.max_freq = f 
        self.hash_map[f].append(x)

    def pop(self) -> int:
        x = self.hash_map[self.max_freq].pop()
        self.counter[x] -= 1
        if not self.hash_map[self.max_freq]:
            self.max_freq -= 1
        return x

# Your FreqStack object will be instantiated and called as such:
# obj = FreqStack()
# obj.push(val)
# param_2 = obj.pop()

150. 逆波兰表达式求值(model-III 算式题)

题目链接:150. 逆波兰表达式求值
题目大意:根据 逆波兰表示法,求表达式的值。有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。注意 两个整数之间的除法只保留整数部分。可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

例如:

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

输入: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

解题思路 :注意:python3 的地板除 “//” 是整数除法, “-3 // 2 = -2” ;python3 的除法 “/” 是浮点除法, “-3 / 2 = -1.5” ; 应该向0取整 即 int(-1.5) = -1。 这道题的 try except finally 结构也很有特色和实用性。

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        option = {
            "+":add,"-":sub,"*":mul,"/":lambda x,y:int(x/y),
        }

        stack =list()
        for token in tokens:
            try:
                num = int(token)
            except ValueError:
                num2 = stack.pop()
                num1 = stack.pop()
                num = option[token](num1,num2)
            finally:
                stack.append(num)
        return stack[0]

227. 基本计算器 II(model-III 延展1)

题目链接:227. 基本计算器 II
题目大意:给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。你可以假设给定的表达式总是有效的。所有中间结果将在 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1] [231,2311] 的范围内。注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

例如:

输入:s = "3+2*2"
输出:7

输入:s = " 3+5 / 2 "
输出:5

解题思路:很不错的一道题 相对于 150. 逆波兰表达式求值 中的字符串中数字和预算符号顺序,这道题看得更舒服一些。

class Solution:
    def calculate(self, s: str) -> int:
        n = len(s)
        # 一个装数字 一个灵活变动存储符号
        stack,op = list(),'+'
        num = 0
        for i,ch in enumerate(s):
            if ch.isdigit():
                num = 10*num + int(ch)
            if i == n-1 or ch in '+-*/':
                if op == '+':
                    stack.append(num)
                elif op == '-':
                    stack.append(-num)
                elif op == '*':
                    stack.append(stack.pop()*num)
                elif op == '/':
                    stack.append(int(stack.pop()/num))
                op,num = ch,0
        return sum(stack)

224. 基本计算器(model-III 延展2)

题目链接:224. 基本计算器
题目大意:给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

例如:

输入:s = "1 + 1"
输出:2

输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23

解题思路: 如果仅有加减操作时,主要的便是 优先展开括号内内容进行计算 相对于 227. 基本计算器 II 此题的字符串更简单一些,这道题可以扩展一下,将加减扩展至 乘除求余等复杂操作,不过这就需要进行各种运算优先级的设定了。

class Solution:
    def calculate(self, s: str) -> int:
        stack,sign = list([1]),1
        # print(stack)
        ans,num,n = 0,0,len(s)
        for i,ch in enumerate(s):
            if ch.isdigit():
                num = 10*num + int(ch)
            if i == n-1 or ch in '+-()':
                ans += sign * num
                if ch == '+':
                    sign = stack[-1]
                elif ch == '-':
                    sign = -stack[-1]
                elif ch == '(':
                    stack.append(sign)
                elif ch == ')':
                    stack.pop()
                num = 0
        return ans

总结

  这次 12 道题,继续加油,再把速度往上提一提,努力,奋斗。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值