【LeetCode刷题】栈与队列专题

前言:之前对栈与队列一直停留在表层概念的理解上,不知道怎么用,借刷题的机会来深入理解一下。

20 有效括号

20. 有效括号
分析:

  • 括号匹配后进先出,很符合栈的特点。
  • Python 中 list就可以实现.pop的方法,后进先出。
  • False 的几种情况:
    • ①字符缺失
    • ②字符不匹配
    • ③遍历字符串后stack中还有值
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


if __name__ == '__main__':
    sol=Solution()
    s = '(({[]()}])'
    a = sol.isValid(s)
    print(a)

# 用字典来实现,差别不大
class Solution:
    def isValid(self, s: str) -> bool:
        stack=[]
        mapping={'(':')','{':'}','[':']'}
        for item in s:
            if item in mapping.keys():
                stack.append(mapping[item])
            elif not stack or stack[-1]!= item:
                return False
            else:
                stack.pop()
        return True if not stack else False

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

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

栈版本

分析:消消乐,相邻相消,还是后进先出,用栈来解决。

  • 老方法,还是用list作为栈。
  • 匹配到相同的就.pop不同就.append(item)
  • 最后输出字符串,把list转化为字符串用''.join(res)来实现
class Solution:
    def removeDuplicates(self, s: str) -> str:
        res=[]
        for item in s:
            if res and res[-1]==item:
                res.pop()
            else:
                res.append(item)
        return ''.join(res)

双指针版本

分析:

  • 双指针的一种思想是:快指针探索,慢指针操作。
  • 把探索的值赋给待操作的值
    • 当和前一位值相同时,消掉,慢指针退回一格
    • 当和前一位值不相同时,慢指针前进一格
    • 直到探索完str的全部值为止
class Solution:
    def removeDuplicates(self, s: str) -> str:
        res=list(s)
        s=0
        length=len(res)

        for f in range(length):
            res[s]=res[f]

            if s>0 and res[s]==res[s-1]:
                s-=1
            else:
                s+=1
                
        return ''.join(res[0:s])

150. 逆波兰表达式求值

150. 逆波兰表达式求值

背景知识:

逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  • 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。

分析:这道题没什么好犹豫的,栈字都已经明晃晃地写出来了,重点是理解一下后缀表达式RPN(Reverse Polish notation)。规则也比较清晰:

  • 遇到数字就压入栈;
  • 遇到符号就弹出最后的两个数字进行计算;
  • 然后把计算结果再压入栈.
  • 这里把字符形式的符号变成标准符号用了eval函数
  • 有一个小坑:计算的时候两个数字不要弄反,先pop出来的数字放在表达式最后一位。
from builtins import str
from typing import List


class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        res = []
        symbols = list('+-*/')
        for item in tokens:
            if item in symbols:
                f, s = res.pop(), res.pop()
                res.append(int(eval(f'{s}{item}{f}')))# 小坑在这里,注意细节
            else:
                res.append(item)
        return int(res.pop())


if __name__ == '__main__':
    sol = Solution()
    str = ["4", "13", "5", "/", "+"]
    a = sol.evalRPN(str)
    print(a)

239. 滑动窗口最大值

239. 滑动窗口最大值
这个题的难度等级是hard,感觉可以不做了。

暴力解法

这个题乍看不难,但暴力解法不出意料的超时了…

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        res = []
        temp = nums[0:k]
        res.append(max(temp))
        if k >= len(nums):
            return res
        else:
            for i in nums[k:]:
                temp.pop(0)
                temp.append(i)
                print(temp)
                res.append(max(temp))
            return res

在这里插入图片描述
最后执行的输入长这样:
在这里插入图片描述

用队列求解

分析:这里队列并不维护k个元素,而仅保留可能成为最大值的元素。
最后这种方法勉勉强强地通过了。
这里用到的队列有三个函数:

  • pop 弹出,当滑动窗口移除的值位于队首时执行此操作
  • push保证push后最大值在队列最前端,如果队列最后的值比push进的值小,则删除队列最末一位的值,循环操作,直到队列前方无值大于push进的值为止。这个操作是保证队列降序的关键。
  • front最大的数值位于队列最前端,返回便是了。
class MyQueue:
    def __init__(self):
        self.queue = []

    def pop(self, value):
        if self.queue and value == self.queue[0]:
            self.queue.pop(0)

    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()
        res = []
        for i in range(k):
            que.push(nums[i])
        res.append(que.front())
        for i in range(k, len(nums)):
            que.pop(nums[i - k])
            que.push(nums[i])
            res.append(que.front())
        return res

在这里插入图片描述

更好的版本

  • TODO:官方题解的方法似乎都挺快的,回头仔细看看
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        q = collections.deque()
        for i in range(k):
            while q and nums[i] >= nums[q[-1]]:
                q.pop()
            q.append(i)

        ans = [nums[q[0]]]
        for i in range(k, n):
            while q and nums[i] >= nums[q[-1]]:
                q.pop()
            q.append(i)
            while q[0] <= i - k:
                q.popleft()
            ans.append(nums[q[0]])
        
        return ans

347. 前 K 个高频元素

347. 前 K 个高频元素

偷懒的内置函数版本

python 统计list中各个元素出现的频数

统计list中元素出现的频数有三种:

  • 利用字典统计
  • 利用Python的collection包下的Counter类统计
  • 利用Python的pandas包下的value_counts类统计
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        return [i[0] for i in Counter(nums).most_common(k)]

优先级队列版本

python数据结构——优先级队列

优先级队列是个披着队列外衣的堆。

本题用小顶堆 ,可以用heapq来实现堆结构。

补充知识:
python字典中get()函数的用法总结
dict[i]=dict.get(i,0)+1
Python heapq库的用法介绍

#时间复杂度:O(nlogk)
#空间复杂度:O(n)
import heapq
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        #要统计元素出现频率
        map_ = {} #nums[i]:对应出现的次数
        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: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
                heapq.heappop(pri_que)
        
        #找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
        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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值