代码随想录算法训练营第十一天 | 150.逆波兰表达式求值 239.滑动窗口最大值 347.前 K 个高频元素

LeetCode 150.逆波兰表达式求值:

文章链接
题目链接:150.逆波兰表达式求值

思路:

① 使用栈进行运算,遇到操作数入栈,遇到运算符出栈两个元素后入栈
② 需要注意的是:所给的算式为string类型,需要转换为 int / long long类型后入栈;且题目要求除法为向零截断,操作数可能为负数(-2 / 3 = -1),因此除法需要先判断操作数的正负后再进行除法运算
逆波兰表达式

  • 是一种后缀表达式,也就是运算符写在后面的表达式。比如(1 + 2) * (3 + 4) 转换为逆波兰表达式即 ( ( 1 2 + ) ( 3 4 + ) * )。
  • 优点如下:
  • 去掉括号后无歧义,即去掉括号后也可以按照次序计算出正确结果
  • 使用用栈运算:遇到数字则入栈;遇到运算符弹出栈顶两个数字进行计算,并将结果压入栈中。(第二个弹出的是第一个操作数
"""
方法1:遇到运算符后逐个判断并进行运算
"""
def div(op1, op2):
    return op1 // op2 if op1 * op2 > 0 else -(abs(op1) // abs(op2))
class Solution(object):
    def evalRPN(self, tokens):
        """
        :type tokens: List[str]
        :rtype: int
        """
        stack = []
        op_set = {'+', '-', '*', '/'}
        for token in tokens:
            if token not in op_set:
                stack.append(int(token))
            else:
                op2 = stack.pop()
                op1 = stack.pop()	# 第二个弹出的为第一个操作数
                if token == '+': stack.append(op1 + op2)
                elif token == '-': stack.append(op1 - op2)
                elif token == '*' : stack.append(op1 * op2)
                else:
                    stack.append(div(op1, op2))
        return stack.pop()
"""
方法2:借用operator库和字典实现字符运算符到实际的函数运算
"""
from operator import add, sub, mul
def div(op1, op2):
    return int(op1 / op2) if op1 * op2 > 0 else -(abs(op1) // abs(op2))

class Solution(object):
    op_map = {'+':add, '-':sub, '*':mul, '/':div}
    def evalRPN(self, tokens):

        stack = []
        for token in tokens:
            if token not in self.op_map:
                stack.append(int(token))
            else:
                op2 = stack.pop()
                op1 = stack.pop()   # 后出现的为第一个操作数
                stack.append(self.op_map[token](op1, op2))
        return stack.pop()

感悟:

需要注意细节:将操作数转换为整数类型,第二个弹出的为第一个操作数,正负数的向零截取除法


LeetCode 239.滑动窗口最大值:

文章链接
题目链接:239.滑动窗口最大值

思路:

使用队列保存窗口中的元素,那么滑动窗口的最大值由队列中得到,那么维持一个单调队列,最大值为队首元素,但是push和pop需要对队列进行调整比较麻烦。
那么维持一个“单调双端队列”
① push (value),若队尾元素 < value,出队队尾元素直到其值 >= value,从而维持了队列的单调。且需要返回的为滑动窗口最大值,要么出队的元素中没有队首最大值,对返回值无影响;要么出队元素中有队首最大值,那么value成为队首最大值。
② pop(value),队首出队,由于push时将 < push_value的值全部出队了,且出队只能出队首元素,因此value == 队首元素时才出队。(若value < queue.front(),那么该元素在push时已经出队,且对于滑动窗口的最大值无影响;value > queue.front(),这种情况不存在)

from collections import deque
class Myqueue(object):
    def __init__ (self):
        self.queue = deque() # 使用双端队列构造单调队列,左队头,右队尾

    def pop(self, value):
        # 当弹出元素为队首元素时才弹出
        if self.queue and value == self.front():
            self.queue.popleft()

    def push(self, value):
        # push元素到队尾,为了保持单调队列;一直弹出队尾元素直到队尾元素 >= value
        while self.queue and self.queue[-1] < value:
            self.queue.pop()
        self.queue.append(value)

    # 队首元素即窗口的最大元素
    def front(self):
        return self.queue[0]
class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        queue = Myqueue()
        result = []
        # 将前k个加入单调队列中
        for i in range(k):
            queue.push(nums[i])
        result.append(queue.front())    # 保存每个窗口的最大值
        for i in range(k, len(nums)):
            queue.pop(nums[i - k])
            queue.push(nums[i])
            result.append(queue.front())
        return result


感悟:

“单调”双端队列的一种实现和利用


LeetCode 347.前K个高频元素:

文章链接
题目链接:347.前K个高频元素

思路:

① 统计并记录元素的频率和元素本身:map
② 对元素频率进行排序
③ 根据排序后的元素频率得到前 k 个高频元素
采用大小为 k 的优先级队列,其中优先级为频率。使用大小为 k 的小根堆实现该优先级队列(每次压入元素时,直接从堆顶弹出最小值,然后压入元素,从而留下的 k 个元素为频率前 k 的元素)。
需要注意的是python中使用heapq包来实现小根堆。heapqpush(pri_que, value)只能根据value元素建立小根堆(想建立大根堆的话使用value的负值建立小根堆,输出元素时再取负值)。建立优先级队列为heappush(pri_que, (pri, value)),根据 pri 在 pri_que中建立最小堆

import heapq
class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        # 统计频率
        map_num = {}
        for num in nums:
            map_num[num] = map_num.get(num, 0) + 1
        # 构造大小为k的小根堆得到频率前 k 高的元素
        pri_que = []
        for num, fre in map_num.items():
            if len(pri_que) < k:
                heapq.heappush(pri_que, (fre, num))
            elif fre > pri_que[0][0]:
                heapq.heappop(pri_que)
                heapq.heappush(pri_que, (fre, num))
        # 倒序存储堆弹出的元素
        result = [0] * k
        for i in range(k - 1, -1, -1):
            result[i] = heapq.heappop(pri_que)[1]
        return result
"""
方法2:转换字典为fre_num,然后单独对频率进行排序并取排序后前 k 个高频元素
"""
from collections import defaultdict
class Solution(object):
    def topKFrequent(self, nums, k):
        # 统计频率
        map_num = {}
        for num in nums:
            map_num[num] = map_num.get(num, 0) + 1
        # 反转字典 (fre: num)
        fre_num = defaultdict(list)
        for num, fre in map_num.items():
            fre_num[fre].append(num)
        # 对频率进行排序
        fre_key = list(fre_num.keys())
        fre_key.sort(reverse=True)
        # 取出现频率前 k 高的元素
        result = []
        cnt, i = 0, 0
        while cnt < k:
            result += fre_num[fre_key[i]]
            cnt += len(fre_num[fre_key[i]])
            i += 1
        return result


感悟:

优先级队列的实现和字典的反转


学习收获:

① 学会了使用小根堆实现优先级队列
② 学会了“单调”双端队列的一种实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值