赞
踩
栈和队列都是为了保证元素的一种顺序性。(其实递归也是有顺序性的(运行到最后一步,再往前走),所以可用stack来模拟递归过程)
单调队列:队列的性质是先进先出,为什么滑动窗口的最大值这个题用单调递减队列?因为窗口往右边滑动的时候,队列前端的值不在窗口里了,把队列前端的值弹出。右侧的大值,一定可以屏蔽左侧的小值。
为什么862用单调递增队列?因为根据前缀和性质,用队列尾减去队列前,得到的就是这中间值之和,本质上是比较窗口的两端元素。
单调栈:栈的性质是先后进先出,为什么接雨水这个题用单调递减栈?因为需要比较的是相邻(不一定是挨着)的元素,而不是比较窗口两端元素。
规律:求s[i]的左边界和右边界。
目录
接雨水和柱形图最大面积,一个是凹槽,一个是凸起,凹槽是找左边的最大值和右边的最大值,所以不需要哨兵节点。凸起找左边的最小值和右边的最小值,需要哨兵节点。
接雨水问题(python实现单调栈问题)_meng_xin_true的博客-CSDN博客
- class Solution(object):
- def trap(self, height):
- """
- :type height: List[int]
- :rtype: int
- """
- #维护一个单调递减栈
- ans=0
- stack=[]
- for i in range(len(height)):
- while len(stack) and height[i]>height[stack[-1]]:
- cur_index=stack.pop()
- if len(stack)==0:
- break
- left = height[stack[-1]]
- right = height[i]
- distans = i - stack[-1] -1
- cur_ans= (min(left,right)-height[cur_index])*distans
- ans=ans+cur_ans
- stack.append(i)
- return ans
- class Solution:
- def trap(self, height: List[int]) -> int:
- #单调递减栈。栈顶存储最小元素。
- #栈顶元素是凹槽,栈顶前一个元素是左边挡板,当前元素(如果比栈顶大),是右边挡板
- ans=0
- stack=[]
- for i in range(len(height)):
- #如果当前元素大于栈顶,当前元素是右挡板
- right=height[i]
- while stack and right>height[stack[-1]]:
- mid_index=stack.pop() #凹槽
- if not stack:
- break
- left=height[stack[-1]] #倒数第二个元素是左挡板
- h=min(left,right)-height[mid_index]
- w=i-stack[-1]-1
- cur_ans=h*w
- ans+=cur_ans
- stack.append(i)
- return ans
接雨水和柱形图最大面积,一个是凹槽,一个是凸起,凹槽是找左边的最大值和右边的最大值,所以不需要哨兵节点。凸起找左边的最小值和右边的最小值,需要哨兵节点。
https://segmentfault.com/a/1190000022791301
单调递增栈。遍历数组,把数组下标放到stack中,如果当前元素小于stack的栈顶元素,弹出stack栈顶元素作为高度,宽度就是留守的Stack栈顶元素和当前元素下表的差值。
注意哨兵节点的使用。
- class Solution(object):
- def largestRectangleArea(self, heights):
- """
- :type heights: List[int]
- :rtype: int
- """
- size = len(heights)
- # 这里我们使用哨兵节点,这样就能够避免非空判断
- heights = [0] + heights+[0]
- # 数组长度发生了变化
- size += 2
- # 将哨兵先放入栈中
- stack = [0]
- ans = 0
- for i in range(1, size):
- # 与栈顶元素比较,进行判断是否要入栈
- # 出栈的逻辑
- while heights[i] < heights[stack[-1]]:
- cur_height = heights[stack.pop()]
- # 确定宽度
- cur_width = i - stack[-1] - 1
- ans = max(ans, cur_height * cur_width)
- stack.append(i)
- return ans
- class Solution(object):
- def largestRectangleArea(self, heights):
- """
- :type heights: List[int]
- :rtype: int
- """
- ans=0
- heights=[0]+heights+[0]
- stack=[]
- for i in range(len(heights)):
- while len(stack) and heights[i]<heights[stack[-1]]:
- cur_index=stack.pop()
- if len(stack)==0:
- break
- cur_height=heights[cur_index]
- distance=i-stack[-1]-1
- cur_ans=cur_height*distance
- ans=max(ans,cur_ans)
- stack.append(i)
- return ans
遍历每行,以每行为底边,可以看作是求最大柱状图的面积。
当这行中,元素为0的时候,柱状图列表为0,元素为1的时候,直接和上一行的进行累加。
- class Solution(object):
- def maxarea(self,height):
- stack=[]
- height=[0]+height+[0]
- ans=0
- for i in range(len(height)):
- while len(stack) and height[i]<height[stack[-1]]:
- cur_index=stack.pop()
- cur_height=height[cur_index]
- distnce=i-stack[-1]-1
- cur_ans=cur_height*distnce
- ans=max(ans,cur_ans)
- stack.append(i)
- return ans
- def maximalRectangle(self, matrix):
- """
- :type matrix: List[List[str]]
- :rtype: int
- """
- if len(matrix)==0:
- return 0
- res=0
- height=[0 for _ in range(len(matrix[0]))]
- for i in range(len(matrix)):
- cur=matrix[i]
- for j in range(len(cur)):
- if cur[j]=="1":
- height[j]=height[j]+1
- elif cur[j]=="0":
- height[j]=0
- print(height)
- cur_ans=self.maxarea(height)
- res=max(res,cur_ans)
- return res
最大矩形加一行代码。在计算面积的时候限制一下distance和height的大小。
- class Solution(object):
- def maxarea(self,height):
- stack=[]
- height=[0]+height+[0]
- ans=0
- for i in range(len(height)):
- while len(stack) and height[i]<height[stack[-1]]:
- cur_index=stack.pop()
- cur_height=height[cur_index]
- distnce=i-stack[-1]-1
- if distnce>=cur_height:
- cur_ans=cur_height*cur_height
- ans=max(ans,cur_ans)
- stack.append(i)
- return ans
- def maximalSquare(self, matrix):
- """
- :type matrix: List[List[str]]
- :rtype: int
- """
- if len(matrix)==0:
- return 0
- res=0
- height=[0 for _ in range(len(matrix[0]))]
- for i in range(len(matrix)):
- cur=matrix[i]
- for j in range(len(cur)):
- if cur[j]=="1":
- height[j]=height[j]+1
- elif cur[j]=="0":
- height[j]=0
- print(height)
- cur_ans=self.maxarea(height)
- res=max(res,cur_ans)
- return res
暴力解法,时间复杂度o(nk).超时。
- class Solution(object):
- def maxSlidingWindow(self, nums, k):
- """
- :type nums: List[int]
- :type k: int
- :rtype: List[int]
- """
- res=[]
- for i in range(len(nums)-k+1):
- left=i
- right=i+k
- cur_max=nums[left]
- for j in range(left+1,right):
- cur_max=max(cur_max,nums[j])
- res.append(cur_max)
- return res
空间换时间,时间复杂度o(n)
- import collections
- #单调递减队列
- class Solution:
- def maxSlidingWindow(self, nums, k):
- if not nums or k == 0: return []
- deque = collections.deque()
- for i in range(k):
- #前一个数字,小于当前数字。把前一个数字弹出
- while deque and deque[-1] < nums[i]:
- deque.pop()
- deque.append(nums[i])
- print(deque)
- res = [deque[0]]
- #i表示滑动窗口最右边
- for i in range(k, len(nums)):
- #如果队列头部值,是上一个窗口的值,就弹出去。
- if deque[0] == nums[i - k]:
- deque.popleft()
- while deque and deque[-1] < nums[i]:
- deque.pop()
- deque.append(nums[i])
- res.append(deque[0])
- print(i,res,deque)
- return res
- nums=[2,3,4,2,6,2,5,1]
- k=3
- result=Solution().maxSlidingWindow(nums,k)
- print(result)
- class Solution:
- def dailyTemperatures(self, T: List[int]) -> List[int]:
- stack = []
- res = [0] * len(T)
- for i in range(len(T)):
-
- while stack and T[stack[-1]] < T[i]:
- res[stack[-1]] = i - stack[-1]
- stack.pop()
-
- stack.append(i)
- return res
-
-
- 作者:xiao-xue-66
- 链接:https://leetcode-cn.com/problems/daily-temperatures/solution/pythonti-jie-dan-diao-di-jian-zhan-by-xiao-xue-66/
- 来源:力扣(LeetCode)
- 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- class Solution(object):
- def dailyTemperatures(self, T):
- """
- :type T: List[int]
- :rtype: List[int]
- """
- #单调递减栈
- stack=[]
- res=[0 for _ in range(len(T))]
- for i in range(len(T)):
- while len(stack) and T[stack[-1]]<T[i]:
- cur_index=stack.pop()
- cur_res=i-cur_index
- res[cur_index]=cur_res
- stack.append(i)
- return res
https://leetcode-cn.com/problems/find-the-most-competitive-subsequence/solution/python3-shuang-bai-jie-fa-by-ni-jiao-wo-dpma/
- class Solution(object):
- def mostCompetitive(self, nums, k):
- """
- :type nums: List[int]
- :type k: int
- :rtype: List[int]
- """
- #维护一个单调递增栈
- stack=[]
- n=len(nums)
- for i in range(n):
- while len(stack) and stack[-1]>nums[i] and (n-i-1)>= k-len(stack):
- stack.pop()
- stack.append(nums[i])
- return stack[:k]
题解:开始的是我是用的,在一开始,我只是单纯的记录左右括号数量的差值和星的个数。这样做忽略了第三个条件。
要在栈里记录位置信息。
先看一下我最开始的题解
- class Solution(object):
- def checkValidString(self, s):
- """
- :type s: str
- :rtype: bool
- """
- stack=[]
- cnt=0
- for i in range(len(s)):
- cur=s[i]
- if cur=='(':
- stack.append(cur)
- elif cur==')':
- if len(stack)==0:
- #用星号模拟左括号
- if cnt>0:
- cnt=cnt-1
- else:
- stack.pop()
- elif cur=="*":
- cnt=cnt+1
- if len(stack)==0:
- return True
- #用星号模拟右括号
- if len(stack)==cnt:
- return True
- return False
- class Solution(object):
- def checkValidString(self, s):
- """
- :type s: str
- :rtype: bool
- """
- stack=[]
- stack_star=[]
- cnt=0
- for i in range(len(s)):
- cur=s[i]
- if cur=='(':
- stack.append(i)
- elif cur==')':
- if len(stack)==0:
- #用星号模拟左括号
- if len(stack_star)>0:
- stack_star.pop()
- else:
- return False
- else:
- stack.pop()
- elif cur=="*":
- stack_star.append(i)
- if len(stack)==0:
- return True
- #stack不为空的时候
- while stack:
- if not stack_star:
- return False
- elif stack[-1]>stack_star[-1]:
- return False
- else:
- stack.pop()
- stack_star.pop()
- return True
- //最长的有效括号--------------->括号子串,括号子串的结尾一定是')',根据右括号为右端点去寻找合法的最长子串对应的左括号
- //只包含'('和')'
- public int longestValidParentheses(String s) {
- int n = s.length(), maxLen = 0;
- Deque<Integer> stack = new ArrayDeque<>();
- stack.push(-1);
- for (int i = 0; i < n; i++) {
- char ch = s.charAt(i);
- // 如果是左括号直接入栈即可
- if (ch == '(')
- stack.push(i);
- else {
- // 如果是右括号,存在两种情况
- // 1.如果前面可以有左括号和它进行匹配,那么就存在一个由左括号、右括号组成的子串
- // 2.如果前面没有左括号和它进行匹配,那么这个右括号就形成了新的边界。新的子串匹配时,起点必须在该边界右边
- if (stack.size() == 1) {
- // 如果栈的大小为1,说明只存放了边界
- // 说明存放的内容为:边界
- // 相应的做法为边界替换,将旧的边界出栈,然后放入新的边界
- // 说明该右括号找不到和它可以匹配的左括号,那么该子串长度无效。同时这个右括号形成了新的边界(参照物)
- stack.pop();
- stack.push(i);
- } else {
- // 如果栈的大小不为1
- // 说明存放的内容为:边界+左括号索引+左括号索引+左括号索引......
- // 说明可以找到和它进行匹配的左括号
- stack.pop();
- maxLen = Math.max(maxLen,i-stack.peek());
- }
- }
- }
- return maxLen;
- }
- 2
- 踩
- 查看 1 条回复
- 回复
- class Solution(object):
- def nextGreaterElement(self, nums1, nums2):
- """
- :type nums1: List[int]
- :type nums2: List[int]
- :rtype: List[int]
- """
- #找右的下一个更大元素,从后往前遍历,维护一个单调递减栈
- #为什么要从后往前遍历,因为是要找右侧,只有从后往前遍历才能比较到当前元素和右侧元素
-
- #先用单调递减栈找到nums2的下一个更大元素
- stack=[]
- next_max=[-1 for _ in range(len(nums2))]
- for i in range(len(nums2)-1,-1,-1):
- #判断当前元素和右侧元素(右侧元素存储在单调栈中,栈顶是挨着最近的)
- while stack and nums2[i]>stack[-1]:
- stack.pop()
- if stack and nums2[i]<stack[-1]:
- next_max[i]=stack[-1]
- stack.append(nums2[i])
- res=[]
- for i in range(len(nums1)):
- num=nums1[i]
- num_index=nums2.index(num)
- cur_ans=next_max[num_index]
- res.append(cur_ans)
- return res
- class Solution(object):
- def nextGreaterElements(self, nums):
- """
- :type nums: List[int]
- :rtype: List[int]
- """
- #用取模来模拟环形数组
- stack=[]
- n=len(nums)
- res=[-1 for _ in range(len(nums))]
- for i in range(n*2-1,-1,-1):
- #如果当前元素大于栈顶元素
- while stack and nums[i%n]>=stack[-1]:
- stack.pop()
- if stack and nums[i%n]<stack[-1]:
- res[i%n]=stack[-1]
- stack.append(nums[i%n])
- return res
这个题是找的下一个更大/更小元素,和最大宽度坡不一样,最大宽度坡找的不是下一个更大/更小元素。
计算 右侧比5小的第一个数,从右往左遍历,单调递增栈。
入栈:[2],1比2小,2出栈,1入栈[1],4入栈,[1,4],7入栈,[1,4,7],6比7小,7出栈,6入栈,[1,4,6]
为什么是单调递增栈?因为要求的是右边第一个比cur小的数,假设右侧j小于i,i出栈,j入栈,假如i比cur小,那么j也一定比cur小,j更靠近cur,所以可用小数屏蔽大数。
- class Solution:
- def subArrayRanges(self, nums: List[int]) -> int:
- # nums[i]是多少子数组的最大值,以及多少子数组的最小值。相加
- # 求nums[i]是多少子数组的最大值,找到nums[i]左边和右边的边界,2个单调栈
- # 求nums[i]是多少子数组的最小值,找到nums[i]左边和右边的边界,2个单调栈。共4个单调栈
-
- left_max = [-1 for i in range(len(nums))]
- right_max = [-1 for i in range(len(nums))]
- left_min = [-1 for i in range(len(nums))]
- right_min = [-1 for i in range(len(nums))]
-
- # 最大值,往右边可以辐射到哪里,单调递减栈
- stack = []
- for i in range(len(nums) - 1, -1, -1):
- while stack and nums[i] > nums[stack[-1]]:
- stack.pop()
- #当前元素比stack[-1]小,说明从stack[-1]开始已经不是最大值了,右边的边界就是stack[-1]
- if stack and nums[i] <= nums[stack[-1]]:
- right_max[i] = stack[-1]
- else:
- right_max[i] = len(nums)
- stack.append(i)
- # 最大值,往左边可以辐射到哪里,单调递减栈
- stack = []
- for i in range(len(nums)):
- while stack and nums[i] >= nums[stack[-1]]:
- stack.pop()
- # 当前元素比stack[-1]小,说明从stack[-1]开始已经不是最大值了,右边的边界就是stack[-1]
- if stack and nums[i] < nums[stack[-1]]:
- left_max[i] = stack[-1]
- else:
- left_max[i] = -1
- stack.append(i)
- # 最小值,往右边可以辐射到哪里,单调递递增
- stack = []
- for i in range(len(nums) - 1, -1, -1):
- while stack and nums[i] < nums[stack[-1]]:
- stack.pop()
- # 当前元素比stack[-1]大,说明从stack[-1]开始已经不是最小值了,右边的边界就是stack[-1]
- if stack and nums[i] >= nums[stack[-1]]:
- right_min[i] = stack[-1]
- else:
- right_min[i] = len(nums)
- stack.append(i)
- # 最小值,往左边边可以辐射到哪里,单调递增
- stack = []
- for i in range(len(nums)):
- while stack and nums[i] <= nums[stack[-1]]:
- stack.pop()
- # 当前元素比stack[-1]大,说明从stack[-1]开始已经不是最小值了,左边的边界就是stack[-1]
- if stack and nums[i] > nums[stack[-1]]:
- left_min[i] = stack[-1]
- else:
- left_min[i] = -1
- stack.append(i)
- print(left_max)
- print(right_max)
- print(left_min)
- print(right_min)
- ans = 0
- for i in range(len(nums)):
- ans += nums[i] * (i - left_max[i]) * (right_max[i] - i)
- ans -= nums[i] * (i - left_min[i]) * (right_min[i] - i)
- return ans
左边界尽可能靠左,右边界尽可能靠右。
左边界尽可能靠左,所以单调递减栈从0下标开始。
右边界尽可能靠右,所以从右往左遍历
- class Solution:
- def maxWidthRamp(self, A: List[int]) -> int:
- stack = []
- n = len(A)
- for i in range(n):
- if not stack or A[stack[-1]] > A[i]:
- stack.append(i)
-
- res = 0
- i = n - 1
- while i > res: # 当res大于等于i时没必要继续遍历了
- while stack and A[stack[-1]] <= A[i]:
- res = max(res, i - stack[-1])
- stack.pop()
- i -= 1
-
- return res
-
- 作者:Elmer
- 链接:https://leetcode-cn.com/problems/maximum-width-ramp/solution/dan-diao-zhan-python-yi-kan-jiu-dong-by-elmer/
- 来源:力扣(LeetCode)
- 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- class Solution:
- def longestWPI(self, hours: List[int]) -> int:
- n = len(hours)
- score = [1 if h >8 else -1 for h in hours]
- presum = [0] * (n + 1)
- for i in range(1, n+1):
- presum[i] = presum[i-1] + score[i-1]
-
- stack = []
- for i in range(n+1):
- if not stack or presum[stack[-1]] > presum[i]:
- stack.append(i)
- ans = 0
- i = n
- while i > ans:
- while stack and presum[stack[-1]] < presum[i]:
- ans = max(ans, i - stack.pop())
- i -= 1
- return ans
-
-
-
- 作者:shzi
- 链接:https://leetcode-cn.com/problems/longest-well-performing-interval/solution/dan-diao-zhan-by-shzi/
- 来源:力扣(LeetCode)
- 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- class Solution:
- def longestWPI(self, hours: List[int]) -> int:
- #前缀和求出来当前i以及以前,劳累的天数是不是严格大于不劳累的天数
- #如果不把presum的长度+1,那么要计算j到i之间的天数,应该是presum[i]-presum[j-1],不好处理边界
- #为什么是presum[i]-presum[j-1],而不是presum[i]-presum[j],因为要求的区间包括j,要从j的前一位开始减。
- n = len(hours)
- score = [1 if h >8 else -1 for h in hours]
- presum = [0] * (n + 1)
- for i in range(1, n+1):
- presum[i] = presum[i-1] + score[i-1]
- print(presum)
- #presum[i] - presum[j] > 0,表示「劳累的天数」是严格 大于「不劳累的天数」
- #j小于i且presum[j]小于presum[i],变成了962
- #单调递减栈
- stack=[]
- for i in range(len(presum)):
- if not stack or presum[i] < presum[stack[-1]]:
- stack.append(i)
- print(stack)
- res=0
- for i in range(len(presum)-1,-1,-1):
- while stack and presum[i] > presum[stack[-1]]:
- res=max(res,i-stack[-1])
- stack.pop()
- return res
- class Solution(object):
- def calculate(self, s):
- """
- :type s: str
- :rtype: int
- """
- #去掉空格
- s = s.replace(" ","")
- from collections import deque
- # 运算符号优先级
- hashmap = {'-': 1,
- '+': 1,
- '*': 2,
- '/': 2,
- '%': 2,
- '^': 3,
- }
-
- # 存放所有数字
- nums = deque([0]) # 为了防止第一个数为负数,先往nums加个0
- # 存放所有的操作
- ops = deque([])
-
- def calc(nums, ops):
- if not nums or len(nums) < 2 or not ops:
- return
- b, a = nums.pop(), nums.pop()
- op = ops.pop()
- ans = 0
- if op == '+':
- ans = a + b
- elif op == '-':
- ans = a - b
- elif op == '*':
- ans = a * b
- elif op == '/':
- ans = a // b
- elif op == '^':
- ans = int(pow(a, b))
- elif op == '%':
- ans = a % b
- nums.append(ans)
-
- n = len(s)
- i = 0
- while i < n:
- c = s[i]
- #左括号入栈ops
- if c == '(':
- ops.append(c)
- #右括号计算
- elif c == ')':
- while ops:
- if ops[-1] != '(':
- calc(nums, ops)
- else:
- ops.pop()
- break
- else:
- #如果是数字,入栈nums
- if c.isdigit():
- tmp = c
- j = i + 1
- #比如几位连在一起的,“150”
- while j < n and s[j].isdigit():
- tmp += s[j]
- j += 1
- nums.append(int(tmp))
- i = j - 1
- else:
- #处理特殊case,如果当前是,加号或者减号,但是前面不是数字的时候
- if i > 0 and s[i - 1] in '(+-':
- nums.append(0)
- #如果是 加号,或者减号
- while ops and ops[-1] != '(':
- #前一位如果小于等于当前的运算优先级,就计算出来。
- #例子:s="2-1+2"
- prev = ops[-1]
- if hashmap[prev] >= hashmap[c]:
- calc(nums, ops)
- else:
- break
- ops.append(c)
- i += 1
-
- while ops and ops[-1] != '(':
- calc(nums, ops)
- return nums[-1]
- class Solution:
- def robotWithString(self, s: str) -> str:
- ans = []
- cnt = Counter(s)
- min = 0 # 剩余最小字母
- st = []
- for c in s:
- cnt[c] -= 1
- while min < 25 and cnt[ascii_lowercase[min]] == 0:
- min += 1
- st.append(c)
- while st and st[-1] <= ascii_lowercase[min]:
- ans.append(st.pop())
- return ''.join(ans)
-
- 作者:endlesscheng
- 链接:https://leetcode.cn/problems/using-a-robot-to-print-the-lexicographically-smallest-string/solution/tan-xin-zhan-by-endlesscheng-ldds/
- 来源:力扣(LeetCode)
- 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。