LeetCode刷题5:栈与队列篇

本文介绍了使用栈和队列实现数据结构的经典算法,如用两个栈模拟队列,用两个队列实现栈,以及有效括号、删除字符串相邻重复项、逆波兰表达式求值、滑动窗口最大值和找到前K个高频元素的问题。文章提供了详细的解题思路和Python代码实现。
摘要由CSDN通过智能技术生成

提示0:抱歉来晚了,后面坚决准时更新~
提示1:本篇共7道力扣题目供大家食用,时间自行把控~

算法刷题系列


作者有话说

  1、本篇是算法刷题系列文章的第 5,写此系列的目的是为了让自己对题目的理解更加深刻。

  2、本系列博客主要参考了卡哥的 代码随想录博客 以及 卡哥本人B站讲解的视频 代码随想录B站视频 ,强烈推荐给大家,因为本人学习中 Python为主,因此博客主要由 Python 代码呈现给大家,需要其他语言的版本,卡哥博客链接自取。


一、栈与队列

1.1 栈

  先进后出,这里有ABCD 四个数据,依次压入栈中,则栈中的元素是 DCBA ,然后依次出栈,出栈顺序的是:D --> C --> B --> A 。

1.2 队列

  先进先出,这里还以上述的例子为例,有ABCD 四个数据,依次压入队中,则队中的元素是 ABCD ,然后依次出队,出队顺序的是:A --> B --> C --> D 。

二、经典题目

2.1 LeetCode232. 用栈实现队列

  • 原题地址: 232. 用栈实现队列
  • 题目描述: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
  • 解题思路: 一个栈代表入栈,一个栈代表出栈,从而实现栈模拟队列。
  • 代码如下:
class MyQueue:

    def __init__(self):
        self.stack_in = []
        self.stack_out = []
        
    def push(self, x: int) -> None:
        self.stack_in.append(x)

    def pop(self) -> int:
        if self.empty():
            return None
        if self.stack_out:
            return self.stack_out.pop()
        else:
            for i in range(len(self.stack_in)):
                self.stack_out.append(self.stack_in.pop())
            return self.stack_out.pop()
            
    def peek(self) -> int:
        result = self.pop()
        self.stack_out.append(result)
        return result
    def empty(self) -> bool:
        return not (self.stack_in or self.stack_out)

2.2 LeetCode225. 用队列实现栈

  • 原题地址: 225. 用队列实现栈
  • 题目描述: 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
  • 解题思路: 栈的核心是:“先进后出”,而队列是:“先进先出”。此题的核心在于如何模拟出栈,这里给出的方法是:假如队列中有size个元素,我们让size-1个元素先出队再入队,这样就可以实现用队列模拟栈的效果。
  • 代码如下:
class MyStack:

    def __init__(self):
        self.queue = []

    def push(self, x: int) -> None:
        self.queue.append(x)

    def pop(self) -> int:
        if self.empty():
            return None
        size = len(self.queue)
        for i in range(size-1):
            self.queue.append(self.queue.pop())
        return self.queue.pop()

    def top(self) -> int:
        if self.empty():
            return None
        return self.queue[-1]

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

2.3 LeetCode20. 有效的括号

  • 原题地址: 20. 有效的括号
  • 题目描述: 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合;
  • 左括号必须以正确的顺序闭合;
  • 每个右括号都有一个对应的相同类型的左括号。
  • 解题思路: 遍历输入字符串,如果是 ([{ 等左括号,便将对应的右括号压入栈中,当遍历的是右括号时,与栈中的元素进行消消乐,若相同栈中元素弹出进入下一轮,若不同直接返回 False。当循环遍历完成后,要考虑栈是否为空的情况,为空返回 True ,否则返回 False
  • 代码如下:

法1代码:

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

法2代码:

def isValid(self, s: str) -> bool:
   if len(s) % 2 != 0:
       return False
   stack = list()
   hashmap = {
       ")": "(",
       "]": "[",
       "}": "{",
   }
   for ch in s:
       if ch in hashmap:
           if not stack or stack[-1] != hashmap[ch]:
               return False
           stack.pop()
       else:
           stack.append(ch)
   return not stack

2.4 LeetCode1047. 删除字符串中的所有相邻重复项

  • 原题地址: 1047. 删除字符串中的所有相邻重复项
  • 题目描述: 给出由小写字母组成的字符串 S重复项删除操作 会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
  • 解题思路: 借助栈这个数据结构来攻克这个题,思想有点类似于有效的括号那道题,我们先遍历元素,入栈,然后遍历下一个,若与栈顶元素相同则消消乐,否则压入栈中,继续遍历。重复以上操作,直到遍历完全部的元素。
    在这里插入图片描述
  • 代码如下:
class Solution:
    def removeDuplicates(self, s: str) -> str:
        stack = []
        for ch in s:
            if stack and stack[-1] == ch:
                stack.pop()
            else:
                stack.append(ch)
        return "".join(stack)  # 字符串拼接

2.5 LeetCode150. 逆波兰表达式求值

  • 原题地址: 150. 逆波兰表达式求值
  • 题目描述: 根据 逆波兰表示法,求表达式的值。
    有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
  • 逆波兰记法: 逆波兰记法中,操作符置于操作数的后面。例如表达“三加四”时,写作“3 4 +”,而不是“3 + 4”。如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的“3 - 4 + 5”在逆波兰记法中写作“3 4 - 5 +”:先3减去4,再加上5。使用逆波兰记法的一个好处是不需要使用括号。摘自百度百科
  • 解题思路: 借助栈,遍历token,遇到数字压入栈中,遇到运算符弹出两个元素进行计算,然后将计算结果重新压入栈中。重复上述操作,直到遍历结束。【2版代码】
  • 代码如下:
class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        for ch in tokens:
            if ch not in {"+", "-", "*", "/"}:
                stack.append(ch)
            else:
                num1, num2 = stack.pop(), stack.pop()
                num1, num2 = int(num1), int(num2)
            if (ch == "+"): stack.append(num2 + num1)
            if (ch == "-"): stack.append(num2 - num1)
            if (ch == "*"): stack.append(num2 * num1)
            if (ch == "/"): stack.append(num2 / num1)
        return int(stack.pop()) 
from operator import add, sub, mul
class Solution:
    op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y)}
    
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        for token in tokens:
            if token not in {'+', '-', '*', '/'}:
                stack.append(int(token))
            else:
                op2 = stack.pop()
                op1 = stack.pop()
                stack.append(self.op_map[token](op1, op2)) 
        return stack.pop()

2.6 LeetCode239. 滑动窗口最大值

  • 原题地址: 239. 滑动窗口最大值
  • 题目描述: 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值
  • 解题思路: 此题借助单调栈的思路去解决,博主也还在理解中,这里给出卡哥的视频讲解,大家去听一听。卡哥视频地址
  • 代码如下: 卡哥的python版本,注释很详细,大家一起理解一下。
from collections import deque


class MyQueue: #单调队列(从大到小
    def __init__(self):
        self.queue = deque() 
    def pop(self, value):
        if self.queue and value == self.queue[0]:
            self.queue.popleft()
    def push(self, value):
        while self.queue and value > self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)
    def front(self):
        return self.queue[0]
    
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        que = MyQueue()
        result = []
        for i in range(k): #先将前k的元素放进队列
            que.push(nums[i])
        result.append(que.front()) #result 记录前k的元素的最大值
        for i in range(k, len(nums)):
            que.pop(nums[i - k]) #滑动窗口移除最前面元素
            que.push(nums[i]) #滑动窗口前加入最后面的元素
            result.append(que.front()) #记录对应的最大值
        return result

2.7 LeetCode347. 前 K 个高频元素

  • 原题地址: 347.前 K 个高频元素
  • 题目描述: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
  • 解题思路: 题目需要做3件事:1、统计元素出现的频率;2、排序;3、找出前K个高频元素。使用字典结构去处理,key代表元素,value统计频率。因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。
  • 代码如下:
import heapq
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        #要统计元素出现频率
        map_ = {} 
        for i in range(len(nums)):
            map_[nums[i]] = map_.get(nums[i], 0) + 1
        
        #对频率排序
        #定义一个小顶堆,大小为k
        pri_que = [] #小顶堆
        
        #用固定大小为k的小顶堆,扫描所有频率的数值
        for key, freq in map_.items():
            heapq.heappush(pri_que, (freq, key))
            if len(pri_que) > k: 
                heapq.heappop(pri_que)
        result = [0] * k
        for i in range(k-1, -1, -1):
            result[i] = heapq.heappop(pri_que)[1]
        return result

总结

  栈与队列篇 到这里就结束了,最后两题有一定的难度大家花一点时间多看看体会一下。最后,若文章中有表述不当的地方还望大家多多指出,二叉树篇见吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想拯救世界_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值