LeetCode冲刺:Day43

发布于 2022-12-20  1072 次阅读


单调栈

顾名思义,首先是一个栈,然后在使用时遵循一定规律。一般是新元素入栈前,对比栈顶元素和新元素的关系,满足一定条件则不断弹出栈顶元素,做业务处理,然后才新元素入栈。

通常用于寻找下/前一个的更大/小元素,或者滑动窗口的最大/小值。

1. 下一个更大元素 II

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

代码

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        stack = [] # 保存下标和元素
        n = len(nums)
        ans = [-1 for i in range(n)]
        for i in range(2 * n):
            j = i % n
            while stack and stack[-1][1] < nums[j]:
                originPos, _ = stack.pop()
                ans[originPos] = nums[j]
            stack.append([j, nums[j]])
        return ans

2. 接雨水

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

只有在遇到的下一个元素大于当前元素时,才有可能产生一个可以存水的图形,所以使用单调栈。

当左侧元素小于自身,右侧元素大于自身时,对存水量的贡献暂时为负。这部分需要好好体会。

代码

class Solution:
    def trap(self, height: List[int]) -> int:
        stack = []
        ans = 0
        n = len(height)
        for index in range(n):
            h = height[index]
            while stack and height[stack[-1]] <= h:
                rightEdge = stack.pop()
                if stack:
                    ans += (min(height[stack[-1]], h) - height[rightEdge]) * (index - stack[-1] - 1)
            stack.append(index)
        return ans

3. 每日温度

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

比较明显的可以用单调栈。

代码

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        ans = [0 for i in range(n)]
        stack = [] # 存储下标
        for index in range(n):
            while stack and temperatures[stack[-1]] < temperatures[index]:
                preDay = stack.pop()
                ans[preDay] = index - preDay
            stack.append(index)
        return ans

4. 股票价格跨度

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

要注意到当前元素的跨度跟之前元素的跨度之间的关系。

如果当前元素大于前一个元素,那么累加前一个元素的跨度,然后把前一个元素弹出。

代码

class StockSpanner:
    __stack: List[List[int]]
    def __init__(self):
        self.__stack = []

    def next(self, price: int) -> int:
        res = 1
        while self.__stack and self.__stack[-1][1] <= price:
            res += self.__stack.pop()[0]
        self.__stack.append([res, price])
        return res

5. 超级丑数

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

之前在写丑数时就考虑到更多质因数的情况,代码可以直接用了。生成新的丑数的方法是一样的,最小元素逐个乘以十质因数即可。就算再来超超超超超级丑数,也是一样做。

代码

class Solution:
    def nthSuperUglyNumber(self, n: int, primes: List[int]) -> int:
        hp = [1]
        cnt = 0
        records = set()
        while True:
            cur = heappop(hp)
            cnt += 1
            if cnt == n: return cur
            for factor in primes:
                num = cur * factor
                if num not in records:
                    records.add(num)
                    heappush(hp, num)

6. 有序矩阵中第 K 小的元素

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

会错意了,以为要时间复杂度小于 n 的平方。

直接维护一个固定大小的堆就好了。因为要的是第k小的元素,当大小大于k时,我们需要弹出最大元素,所以存储负值。那么最后堆顶元素的负值就是所求。

代码

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        hp = []
        n = len(matrix)
        for i in range(n):
            for j in range(n):
                heappush(hp, -matrix[i][j])
                if len(hp) > k:
                    heappop(hp)
        return -hp[0]

7. 根据字符出现频率排序

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

字符种类有限,先用定长数组统计字符频率,然后用两个元素的数组存储频率(负值)和字符(ascii)的对应关系,并插入堆中。最后逐个出堆并构造字符串。

代码

class Solution:
    def frequencySort(self, s: str) -> str:
        freq = [0 for i in range(123)]
        hp = []
        for ch in s: freq[ord(ch)] += 1
        for i in range(48, 123):
            if freq[i] != 0:
                heappush(hp, [-freq[i], i])
        s = ""
        while hp:
            num, ch = heappop(hp)
            s += chr(ch) * (-num)
        return s

8. 分割数组为连续子序列

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

当遇到一个元素 num 时,我们优先考虑是否存在一个以 num - 1 结尾的序列,如果存在,应当将其加入到该序列末尾,且该序列最好是同类序列中最短的一个;否则,应当新建一个仅包含 num 的序列。最后,根据每个序列的长度,判断能否成功分割。如此一来,我们需要保存每个序列的末尾元素和长度信息(每次获取最小值)。可以使用一个字典,键为末尾元素,值为序列长度。

代码

class Solution:
    def isPossible(self, nums: List[int]) -> bool:
        dic = defaultdict(list)
        for num in nums:
            if dic[num - 1]:
                len = heappop(dic[num - 1])
                heappush(dic[num], len + 1)
            else: heappush(dic[num], 1)
        keys = dic.keys()
        for key in keys:
            if dic[key] and dic[key][0] < 3: return False
        return True