Python 单调栈(单调递减栈)Leetcode739.每日温度 Leetcode42.接雨水

单调栈也是笔试面试的时候经常出现的考点,经典的题目如Leetcode739.每日温度 与 Leetcode42.接雨水,这两个题均是维护一个单调栈

注意:
1)题目一般给的是一个数组,那么栈中存放的可以是数组的下标,也可以是数组元素,这里要区分好。
2)根据题意看是要维护一个单调递增还是单调递减的栈,上述两题均是维护一个单调递减栈

伪代码:
	stack = [] # 创建一个栈,并维护其形成一个单调栈
	ans = [0]*len(数组) or ans = 0 # 看结果需要返回的是一个数还是一个列表,这里以返回一个列表举例 
	for i in range(len(数组)):
		while stack and 数组[i] > 数组[stack[-1]]: # 如果栈非空并且栈顶元素小于数组索引i处元素
			prev_index = stack.pop() # 将元素出栈
			ans[pre_index] = i - pre_index 
		stack.append(i) # 此时符合单调栈,将i索引值推入栈顶
	print(ans)			

Leetcode739.每日温度 题目链接
Python 单调栈(单调递减栈)Leetcode739.每日温度 Leetcode42.接雨水_第1张图片

    def dailyTemperatures(temperatures: List[int]) -> List[int]:
        # 单调栈:时间复杂度O(n),空间复杂度O(n)
        # 遍历每日温度,维护一个单调栈:
        # 若栈为空或者当日温度小于等于栈顶温度则直接入栈
        # 反之,若当日温度大于栈顶温度,说明栈顶元素的升温日找到了,则将栈顶元素出栈,计算其与当日相差的天数即可。
        length  = len(temperatures)
        stack = [] # 栈
        res = [0]*length # 存放结果
        for i in range(length):
            temperature = temperatures[i] # 当日温度
            while stack and temperature> temperatures[stack[-1]]: # 如果当日温度大于栈顶温度,说明找到该日的升温日了
                preIndex = stack.pop() 
                res[preIndex] = i-preIndex
            stack.append(i)
        return res

Leetcode42.接雨水
Python 单调栈(单调递减栈)Leetcode739.每日温度 Leetcode42.接雨水_第2张图片

解法一:单调栈
建议debug一下,会发现,单调栈解法中,code计算ans值是一层一层计算的,如下图所示
Python 单调栈(单调递减栈)Leetcode739.每日温度 Leetcode42.接雨水_第3张图片

    def trap(height: List[int]) -> int:
        # 设计一个单调栈,单调栈中存储的是下标,满足从栈底到栈顶的下标对应的数组height中的元素递减,单调递减栈
        ans = 0 # 记录结果
        stack = [] # 单调栈
        for i,h in enumerate(height): # 同时获得下标和对应的高度
            while stack and h> height[stack[-1]]:# 可以接到雨水的区域一定是高度先减后增的,在单调栈中,栈顶元素永远比它的前一个元素低
            #此时的高度有比栈顶元素高,这样就是一个高低高的区域,故可以得到一个可以接雨水的区域
                top = stack.pop()
                if not stack: # 栈为空,左边不存在最大值,无法接到雨水
                    break
                left = stack[-1] # left成为新的栈顶元素
                currWidth = i-left-1 #获取接雨水区域的宽度
                currHeight = min(height[left], height[i])- height[top] # 能接到雨水的高度
                ans += currWidth*currHeight
            stack.append(i) # 在对下标i出计算能接的雨水量之后,将i入栈,继续遍历后面的下标
        return ans

解法二:找到最高点二分计算左右区间面积

针对最高处左侧的区域,我们可以得出以下结论:能接到雨水的面积只与左边部分有关,如果左半部分比现在的位置高,那么一定能收集到雨水

针对最高处右侧的区域,我们可以得出以下结论:能接到雨水的面积只与右部分有关,如果右半部分比现在的位置高,那么一定能收集到雨水

Python 单调栈(单调递减栈)Leetcode739.每日温度 Leetcode42.接雨水_第4张图片

    def trap(height: List[int]) -> int:
        # 找到数组最大值及其下标,作为边界,将数组分为两份
        max_value = max(height)
        max_index = height.index(max_value) # python 中 a.index(target) 可以返回某一个值的索引
        # print(max_value,max_index)
        result = 0
        # 处理左半部分
        lst1 = height[0:max_index+1] # [0,1,0,2,1,0,1,3]
        for left in range(0,max_index): # 到不了max_index
            for i in range(left,max_index+1):  # 到得了max_index
                if lst1[left]>lst1[i]:
                    result+= lst1[left]-lst1[i]
                else:
                    left = i
            break
        lst2 = height[max_index:] 
        lst2 = lst2[::-1] # 将右半部分反转,这样和左半部分求解相同
        for left in range(0,len(lst2)-1): # 到不了max_index
            for i in range(left,len(lst2)):  # 到得了max_index
                if lst2[left]>lst2[i]:
                    result+= lst2[left]-lst2[i]
                else:
                    left = i
            break
        return result

你可能感兴趣的:(leetcode,python,python,leetcode,算法)