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

503.下一个更大元素II

# 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。
# 数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。
#
# 示例 1:
# 输入: nums = [1,2,1]
# 输出: [2,-1,2]
# 解释: 第一个 1 的下一个更大的数是 2;
# 数字 2 找不到下一个更大的数;
# 第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
#
# 示例 2:
# 输入: nums = [1,2,3,4,3]
# 输出: [2,3,4,-1,4]
#
# 提示:
# 1 <= nums.length <= 104
# -109<= nums[i] <= 109
# 通过次数179,433提交次数270,230

class Solution:
    def nextGreaterElements(self, nums: [int]) -> [int]:
        # dp = [-1] * len(nums)
        # stack = []
        # for i in range(len(nums)*2):
        #     while(len(stack) != 0 and nums[i%len(nums)] > nums[stack[-1]]):
        #             dp[stack[-1]] = nums[i%len(nums)]
        #             stack.pop()
        #     stack.append(i%len(nums))
        # return dp

42. 接雨水

# 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
#
# 示例 1:
# 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
# 输出:6

# 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
# 示例 2:
#
# 输入:height = [4,2,0,3,2,5]
# 输出:9
# n == height.length
# 1 <= n <= 2 * 104
# 0 <= height[i] <= 105
from typing import List

class Solution:
    def trap(self, height: List[int]) -> int:
        # 单调栈
        '''
        单调栈是按照 行 的方向来计算雨水
        从栈顶到栈底的顺序:从小到大
        通过三个元素来接水:栈顶,栈顶的下一个元素,以及即将入栈的元素
        雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度
        雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1(因为只求中间宽度)
        '''
        # stack储存index,用于计算对应的柱子高度
        stack = [0]
        result = 0
        for i in range(1, len(height)):
            # 情况一
            if height[i] < height[stack[-1]]:
                stack.append(i)
            # 情况二
            # 当当前柱子高度和栈顶一致时,左边的一个是不可能存放雨水的,所以保留右侧新柱子
            # 需要使用最右边的柱子来计算宽度
            elif height[i] == height[stack[-1]]:
                stack.pop()
                stack.append(i)
            # 情况三
            else:
                # 抛出所有较低的柱子
                while stack and height[i] > height[stack[-1]]:
                    # 栈顶就是中间的柱子:储水槽,就是凹槽的地步
                    mid_height = height[stack[-1]]
                    stack.pop()
                    if stack:
                        right_height = height[i]
                        left_height = height[stack[-1]]
                        # 两侧的较矮一方的高度 - 凹槽底部高度
                        h = min(right_height, left_height) - mid_height
                        # 凹槽右侧下标 - 凹槽左侧下标 - 1: 只求中间宽度
                        w = i - stack[-1] - 1
                        # 体积:高乘宽
                        result += h * w
                stack.append(i)
        return result

    # 单调栈压缩版
    def trap0(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


    # 4 这个是随想录上面的写法,也超时了,可能是力扣后来追加了案例,不允许这种写法了
    def trap4(self, height: List[int]) -> int:
        size = len(height)
        res = 0
        for i in range(size):
            if i == 0 or i == size - 1:
                continue
            lefth = height[i-1]
            righth = height[i+1]
            for j in range(i):
                if lefth < height[j]:
                    lefth = height[j]
            for k in range(i+2,size):
                if righth < height[k]:
                    righth = height[k]
            rain = min(lefth,righth) - height[i]
            if rain > 0:
                res += rain
        return res

    # 3 应该也可以理解为双指针写法,和4是一样的,但是超时了
    def trap3(self, height: List[int]) -> int:
        size = len(height)
        res = 0
        for i in range(size):
            if i == 0 or i == size - 1:
                continue
            lefth = max(height[:i])
            righth = max(height[i + 1:])
            rain = min(lefth, righth) - height[i]
            if rain > 0:
                res += rain
            return res

    """
       只要记录左边柱子的最高高度和右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。
       当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。
       为了得到两边的最高高度,用双指针遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。把每一个位置的左边
       最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就
       用到了动态规划。

       当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。
       即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);
       从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);
    """
    # 2.这个是卡哥动态规划的解法,感觉这个和双指针区别不大,不过优化了for循环,不用双重循环了
    def trap2(self, height: List[int]) -> int:
        size = len(height)
        lhigh = [0] * size
        rhigh = [0] * size
        lhigh[0] = height[0]
        rhigh[-1] = height[-1]
        for i in range(1, size):
            if height[i] > lhigh[i - 1]:
                lhigh[i] = height[i]
            else:
                lhigh[i] = lhigh[i - 1]
        for i in range(size - 2, -1, -1):
            if height[i] > rhigh[i + 1]:
                rhigh[i] = height[i]
            else:
                rhigh[i] = rhigh[i + 1]
        res = 0
        for i in range(size):
            tmp = min(lhigh[i], rhigh[i]) - height[i]
            if tmp > 0:
                res += tmp
        return res

    # 1.这里是动态规划的写法,参考卡哥C语言的写法,有点点小区别
    def trap1(self, height: List[int]) -> int:
        size = len(height)
        lhigh = [height[0]]
        rhigh = [height[size - 1]]
        for i in range(1, size):
            if height[i] > lhigh[-1]:
                lhigh.append(height[i])
            else:
                lhigh.append(lhigh[-1])
        for i in range(size - 2, -1, -1):
            if height[i] > rhigh[-1]:
                rhigh.append(height[i])
            else:
                rhigh.append(rhigh[-1])
        # 上面计算的时候是append,只能向后追加,这里需要倒转一下数组,数组存的是当前位置的右边最大值
        rhigh.reverse()
        res = 0
        for i in range(size - 1):
            tmp = min(lhigh[i], rhigh[i]) - height[i]
            if tmp > 0:
                res += tmp
        return res

if __name__ == '__main__':
    height = [0,1,0,2,1,0,1,3,2,1,2,1]
    tmp = Solution()
    res = tmp.trap(height)
    print(res)

你可能感兴趣的:(算法,数据结构,排序算法)