SONG Shengjie

List:150. 逆波兰表达式求值,239. 滑动窗口最大值,347.前 K 个高频元素,栈和队列总结

150. 逆波兰表达式求值evaluate-reverse-polish-notation239. 滑动窗口最大值sliding-window-maximum347.前 K 个高频元素top-k-frequent-elements栈和队列总结

150. 逆波兰表达式求值evaluate-reverse-polish-notation

Leetcode Learning Materials

image

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        for char in tokens:
            if char == '+' or char == '-' or char == '*' or char == '/':
                nums2 = stack.pop()
                nums1 = stack.pop()
                if char == '+':
                    stack.append(nums1 + nums2)
                if char == '-':
                    stack.append(nums1 - nums2)
                if char == '*':
                    stack.append(nums1 * nums2)
                if char == '/':
                    stack.append(int(nums1 / nums2))  #两个整数之间的除法总是 向零截断 
            else:
                stack.append(int(char))  #若不将 "3" 和 "2" 转换为整数,在执行加法运算时,Python 会将它们视为字符串进行拼接操作。
        return stack[0]

239. 滑动窗口最大值sliding-window-maximum

Leetcode Learning Materials

image

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        self.res = deque() #使用双端队列,允许在队列的两端(队首和队尾)进行元素的插入和移除操作
        result = []
        for i in range(k): #先将前k的元素放进队列
            self.push(nums[i])  #定义了自己的 push 和 pop 方法,应该使用 self.push 和 self.pop 来调用这些自定义方法
        result.append(self.getmax()) #result 记录前k的元素的最大值

        for i in range(k, len(nums)):
            self.pop(nums[i - k]) #滑动窗口移除最前面元素
            self.push(nums[i]) #滑动窗口前加入最后面的元素
            result.append(self.getmax()) #记录对应的最大值
        return result

    def pop(self, value):
        if self.res and self.res[0] == value:  #每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
            self.res.popleft()

    def push(self, value):  #如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
        while self.res and self.res[-1] < value:  #使用 while 循环,不断移除队尾小于当前值的元素,以保证队列中的元素始终是单调递减的。这样在每次获取最大值时,队首元素就是当前窗口的最大值。
            self.res.pop()
        self.res.append(value)

    def getmax(self) : #查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
        return self.res[0] #用 self.res[0] 来访问队首元素

347.前 K 个高频元素top-k-frequent-elements

Leetcode Learning Materials

image

import heapq
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        mapp = {}
        #统计元素频率:遍历列表 nums,使用字典 mapp 统计每个元素的出现频率。
        for i in range(len(nums)):
            mapp[nums[i]] = mapp.get(nums[i] , 0) + 1
            # mapp.get(nums[i], 0) 表示在字典 mapp 中查找键 nums[i] 对应的值。如果 nums[i] 存在于 mapp 中,就返回该键对应的值;如果 nums[i] 不存在,就返回默认值 0。

        #构建小顶堆:使用 Python 的 heapq 库构建一个大小为 k 的小顶堆,堆中存储元素及其频率的元组。
        pri_que = []  # 初始化一个空列表 pri_que,用于存储小顶堆
        for key, freq in mapp.items():
            heapq.heappush(pri_que, (freq, key)) # 换顺序:使用 heapq.heappush 方法将元素及其频率的元组 (freq, key) 加入小顶堆
                    #堆会按照元组的第一个元素(即频率 freq)进行排序
            if len(pri_que) > k:
                heapq.heappop(pri_que) # 使用 heapq.heappop 方法弹出堆顶元素(频率最小的元素)
        
        #筛选前 k 个高频元素:遍历字典 mapp,将元素及其频率加入小顶堆。当堆的大小超过 k 时,弹出堆顶元素(频率最小的元素),保证堆中始终存储频率最高的 k 个元素。
        result = [0] * k
        for i in range(k-1, -1, -1): #从 k - 1 到 0 的递减整数序列,步长为 -1,小顶堆弹出的元素是按频率从小到大的顺序,而我们需要的结果是按频率从大到小排列
            result[i] = heapq.heappop(pri_que)[1] # 使用 heapq.heappop 方法弹出堆顶元素,并取元组的第二个元素(元素本身)存入结果列表
                    #堆中的元素是元组 (freq, key),其中第一个元素 freq 是频率,第二个元素 key 是元素本身
                    #目标是找出出现频率前 k 高的元素,而不是频率值本身。
        return result

栈和队列总结

Learning Materials

陷阱1:栈是容器适配器,底层容器使用不同的容器,导致栈内数据在内存中不一定是连续分布的。

陷阱2:缺省情况下,默认底层容器是deque,那么deque在内存中的数据分布是什么样的呢? 答案是:不连续的,下文也会提到deque。