代码随想录算法训练营第五十九天 |503. 下一个更大元素 II、42. 接雨水

115 阅读2分钟

503. 下一个更大元素 II

代码随想录文章讲解

单调栈

  • 遍历2遍list(因为nums是circular integer array),下标都是i % len(nums)
  • 剩下的与739. 每日温度类似
 class Solution:
     def nextGreaterElements(self, nums: List[int]) -> List[int]:
         n = len(nums)
         ans = [-1] * n
         mono_stack = []
         
         for i in range(2*n):
             while mono_stack and nums[mono_stack[-1]] < nums[i%n]:
                 prev_index = mono_stack.pop()
                 ans[prev_index] = nums[i%n]
             mono_stack.append(i%n)
 ​
         return ans

42. 接雨水

代码随想录文章讲解

双指针(Time limit exceeded)

  • 首先,如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。 可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。
  • 每列雨水的高度为min(lHeight, rHeight) - height
  • 只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。
  • 要注意第一个柱子和最后一个柱子不接雨水
 # Time complexity: O(N^2)
 # Space complexity: O(1)
 class Solution:
     def trap(self, height: List[int]) -> int:
         res = 0
         for i in range(len(height)):
             # first col and last col cannot trapping rain water
             if i == 0 or i == len(height) - 1:
                 continue
 ​
             height_left = height[i-1]
             height_right = height[i+1]
 ​
             for r in range(i+2, len(height)):
                 if height[r] > height_right:
                     height_right = height[r]
 ​
             for l in range(i-2, -1, -1):
                 if height[l] > height_left:
                     height_left = height[l]
 ​
             h = min(height_left, height_right) - height[i]
             if h > 0:
                 res += h
         
         return res

DP

  • 思路与双指针一致,我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就用到了动态规划。
  • 当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);这样就找到递推公式。
 class Solution:
     def trap(self, height: List[int]) -> int:
         if len(height) <= 2:
             return 0
 ​
         res = 0
         max_left = [0] * len(height)
         max_left[0] = height[0]
 ​
         max_right = [0] * len(height)
         max_right[-1] = height[-1]
 ​
         for l in range(1,len(height)):
             max_left[l] = max(max_left[l-1], height[l])
         
         for r in range(len(height)-2, -1, -1):
             max_right[r] = max(max_right[r+1], height[r])
 ​
         for i in range(len(height)):
             h = max(min(max_left[i], max_right[i]) - height[i], 0)
             res += h
         
         return res

单调栈

  • 首先单调栈是按照方向来计算雨水
  • 使用单调栈内元素的顺序: 从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。
  • 遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度
  • 是用单调栈,其实是通过 长 * 宽 来计算雨水面积的。长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,那么栈里有没有必要存一个pair<int, int>类型的元素,保存柱子的高度和下标呢。其实不用,栈里就存放int类型的元素就行了,表示下标,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。
 class Solution:
     def trap(self, height: List[int]) -> int:
         stack = [0]
         result = 0
         for i in range(1, len(height)):
             while stack and height[i] > height[stack[-1]]:
                 mid_height = stack.pop()
                 if stack:
                     # 雨水高度是 min(凹槽左侧高度, 凹槽右侧高度) - 凹槽底部高度
                     h = min(height[stack[-1]], height[i]) - height[mid_height]
                     # 雨水宽度是 凹槽右侧的下标 - 凹槽左侧的下标 - 1
                     w = i - stack[-1] - 1
                     # 累计总雨水体积
                     result += h * w
             stack.append(i)
         return result