目录
1、当前项向右寻找第一个大于自己的元素——从右向左维护递减栈
2、当前项向右寻找第一个大于自己的元素——从左向右维护递减栈
4、当前项向左寻找第一个大于自己的元素——从右向左维护递减栈
1、当前项向左寻找第一个小于自己的元素——从右向左维护一个递增栈
2、当前项向右寻找第一个小于自己的位置——从左到右维护一个递增栈
3、当前项向右寻找第一个小于自己的元素——从右向左维护一个递增栈
单调栈讲解:
单调栈
单调递增栈和单调递减栈
时间复杂度是O(n)
单调栈原理:
单调递增栈:从 栈底 到 栈顶 递增,栈顶大
单调递减栈:从 栈底 到 栈顶 递减,栈顶小
单调栈模板:
(Python数据结构-单调栈(Monotone Stack) - 简书 (jianshu.com))
递减栈
1、当前项向右寻找第一个大于自己的元素——从右向左维护递减栈
def rightToleft_DownStack(nums):
length = len(nums)
res = [-1]*length
stack = []
#muns=[2,9,3,5,6,4,7]
for i in range(length-1,-1,-1): #从右向左 #找右边大
while stack and stack[-1] <= nums[i]: #不符合递减栈,出栈删除 从大到小
stack.pop()
if stack :
print(stack[-1])
res[i] = stack[-1]
stack.append(nums[i]) #大的先入栈(栈底)
return res
nums=[2,9,3,5,6,4,7]
res=rightToleft_DownStack(nums)
print(res)
2、当前项向右寻找第一个大于自己的元素——从左向右维护递减栈
def leftToright_DownStack(nums):
length = len(nums)
res,stack = [-1]*length,[]
for i in range(length): #从左向右
while stack and nums[stack[-1]]< nums[i]: #找右边大
idx = stack.pop()
res[idx] = nums[i]
stack.append(i)
return res
nums=[2,9,3,5,6,4,7]
res=leftToright_DownStack(nums)
print(res)
3、当前项向左寻找第一个大于自己的元素——从左向右维护递减栈
def leftToright_DownStack2(nums):
length = len(nums)
res,stack = [-1]*length,[]
for i in range(length): #从左向右
while stack and nums[i] >= stack[-1]: #递减都是当前大于栈顶,找左边大
stack.pop()
if stack:
res[i]=stack[-1]
stack.append(nums[i])
return res
nums=[2,9,3,5,6,4,7]
res=leftToright_DownStack2(nums)
print(res)
4、当前项向左寻找第一个大于自己的元素——从右向左维护递减栈
def rightToleft_DownStack2(nums):
length = len(nums)
res = [-1]*length
stack = []
for i in range(length-1,-1,-1): #从右向左维护
while stack and nums[stack[-1]] <= nums[i]: #找左边大
idx = stack.pop()
res[idx] = nums[i]
stack.append(i)
return res
nums=[2,9,3,5,6,4,7]
res=rightToleft_DownStack2(nums)
print(res)
递增栈
1、当前项向左寻找第一个小于自己的元素——从右向左维护一个递增栈
def RightToLeftUpStack2(nums):
length = len(nums)
res,stack = [-1]*length,[]
for i in range(length-1,-1,-1): #从右向左
while stack and nums[i]<=nums[stack[-1]]: #找左边小
idx = stack.pop()
res[idx] = nums[i]
stack.append(i)
return res
nums=[2,9,3,5,6,4,7]
res=RightToLeftUpStack2(nums)
print(res)
2、当前项向右寻找第一个小于自己的位置——从左到右维护一个递增栈
def leftTorightUpStack(nums):
length = len(nums)
res,stack = [-1]*length,[]
for i in range(length): #从左向右
while stack and nums[i] <= nums[stack[-1]]: #找右边小
idx = stack.pop()
res[idx] = nums[i]
stack.append(i)
return res
nums=[2,9,3,5,6,4,7]
res=leftTorightUpStack2(nums)
print(res)
3、当前项向右寻找第一个小于自己的元素——从右向左维护一个递增栈
def RightToLeftUpStack(nums):
length = len(nums)
res,stack = [-1]*length,[]
for i in range(length-1,-1,-1): #从右向左
while stack and nums[i]<= stack[-1]: #找右边小
stack.pop()
if stack:
res[i] = stack[-1]
stack.append(nums[i])
return res
nums=[2,9,3,5,6,4,7]
res=RightToLeftUpStack2(nums)
print(res)
力扣题目:
496. 下一个更大元素 I
nums1
中数字 x
的 下一个更大元素 是指 x
在 nums2
中对应位置 右侧 的 第一个 比 x
大的元素。
给你两个 没有重复元素 的数组 nums1
和 nums2
,下标从 0 开始计数,其中nums1
是 nums2
的子集。
对于每个 0 <= i < nums1.length
,找出满足 nums1[i] == nums2[j]
的下标 j
,并且在 nums2
确定 nums2[j]
的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1
。
返回一个长度为 nums1.length
的数组 ans
作为答案,满足 ans[i]
是如上所述的 下一个更大元素 。
题目
示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出:[-1,3,-1] 解释:nums1 中每个值的下一个更大元素如下所述: - 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。 - 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。 - 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
示例 2:
输入:nums1 = [2,4], nums2 = [1,2,3,4]. 输出:[3,-1] 解释:nums1 中每个值的下一个更大元素如下所述: - 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。 - 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1
答案:
提供两种接替方案,第一种是根据下表进行单调栈、第二种是根据元素值。其中方法二为官方答案。
def nextGreaterElement(nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
len1,len2=len(nums1),len(nums2)
stack = []
res = {}
# 方法一
# for i in range(len2):
# while stack and nums2[i]> nums2[stack[-1]]:
# idx = stack.pop()
# res[nums2[idx]] = nums2[i]
# if (stack and nums2[i] < nums2[stack[-1]]):
# print(nums2[stack[-1]])
# res[nums2[stack[-1]]] = -1
# stack.append(i)
# #最后一个数字无法计算,手动添加
# res[nums2[-1]] = -1
# 方法二,官方答案
for num in reversed(nums2): #从右向左维护
while stack and num > stack[-1]: # 寻找右面最大
stack.pop()
res[num] = stack[-1] if stack else -1
stack.append(num)
return [res[j] for j in nums1]
nums1=[4,1,2]
nums2=[1,3,4,2]
print(nextGreaterElement(nums1,nums2))
解析:
Q1:考虑方法一和方法二的区别:
方法一是根据索引进行求解,即正向求解(维护方向和求解方向一致),因此会导致出栈元素即为所求,需要保存结果。
方法二是根据数值进行求解,及反向求解(维护方向和求解方向相反),因此出栈元素为需要删除的元素,不需要保存。而栈顶元素需要保存到结果中。
由于本题用到了map(索引),将nums2的数值作为索引,因此使用方法二个人觉得更加简洁。并且方法二可以对无解元素直接赋值-1,而方法一无解元素不会进入while循环。当然也可以考虑在对map进行初始化时赋值-1。
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
答案:
class Solution(object):
def nextGreaterElements(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
length = len(nums)
res,stack = [-1]*length,[]
for i in range(2*(length-1),-1,-1)
while stack and stack[-1] <= nums[i%length]:
#判断时相当于i%length取值范围[0-2],
# # 从下标2开始判断和后面元素
# #从而可知类似判断需要先对后面的栈赋值,或者正常判断只输出0-2下标部分。
stack.pop()
if stack:
res[i%length] = stack[-1]
stack.append(nums[i%length])
return res
解析:
官方答案,隐形拉直nums=[1,2,1]===>[1,2,1,1,2]
- 初始化取n = length
- 实际上实现的是先[length/2-1](也就是用i%(length)得到的)位置入栈,然后判断[0]位置。
- 然后判断[2]和[0] 利用取模操作
- 抽象出来也就是,先[n/2-1]坐标元素入栈,然后判断[n/2-2]坐标元素
- 以此类推判断[n/2-i]和[n/2-i+1]的元素直至[n/2-1]为[0]为止。
- 开始第二阶段循环,判断[0]和[n]
- 直至[n]为length/2位置
1475. 商品折扣后的最终价格
题目:
给你一个数组 prices
,其中 prices[i]
是商店里第 i
件商品的价格。
商店里正在进行促销活动,如果你要买第 i
件商品,那么你可以得到与 prices[j]
相等的折扣,其中 j
是满足 j > i
且 prices[j] <= prices[i]
的 最小下标 ,如果没有满足条件的 j
,你将没有任何折扣。
请你返回一个数组,数组中第 i
个元素是折扣后你购买商品 i
最终需要支付的价格。
示例 1:
输入:prices = [8,4,6,2,3] 输出:[4,2,4,2,3] 解释: 商品 0 的价格为 price[0]=8 ,你将得到 prices[1]=4 的折扣,所以最终价格为 8 - 4 = 4 。 商品 1 的价格为 price[1]=4 ,你将得到 prices[3]=2 的折扣,所以最终价格为 4 - 2 = 2 。 商品 2 的价格为 price[2]=6 ,你将得到 prices[3]=2 的折扣,所以最终价格为 6 - 2 = 4 。 商品 3 和 4 都没有折扣。
示例 2:
输入:prices = [1,2,3,4,5] 输出:[1,2,3,4,5] 解释:在这个例子中,所有商品都没有折扣。
示例 3:
输入:prices = [10,1,1,6] 输出:[9,0,1,6]
答案:
class Solution(object):
def finalPrices(self, prices):
"""
:type prices: List[int]
:rtype: List[int]
"""
length = len(prices)
res,stack = [0]*length,[]
# Python中把一个list赋给新的list,改变其中一个list,两个list都会改变
#从右向左维护
for i in range(length-1,-1,-1):
while stack and stack[-1] > prices[i]:
stack.pop()
res[i] = prices[i]-stack[-1] if stack else prices[i]
stack.append(prices[i])
return res
解析:
翻译:
- j>i向右寻找 prices[j]<=prices[i]的最小下标 --》 第一个最小值
- 单调栈(递增)
- 返回数组第 i 个元素是折扣后你购买商品 i 最终需要支付的价格 prices[i] = prices[i]-prices[j]
42. 接雨水
题目:
给定 n
个非负整数表示每个宽度为 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
答案:
class Solution(object):
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
ans = 0
length = len(height)
stack = []
if length <3:
return 0;
#向右找到第一个大,递减
#需要长度,下标
#从左向右遍历
for i in range(length):
while stack and height[stack[-1]] < height[i]:
idx = stack.pop()
if stack:
ans += (min(height[i], height[stack[-1]]) - height[idx])*(i - stack[-1] - 1)
stack.append(i)
return ans
解析:
首先,如果只有两个柱子就无法接雨水。
其次,根据单调栈的特点,不难发现height[idx]小于height[stack[-1]]和height[i],通俗来讲就是出现一个坑,可以接雨水。注意:如果栈为空就意味着水会溜走,不用计算。
最后,把每个坑里的水加在一起
心得:
901. 股票价格跨度
题目:
设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。
当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
-
例如,如果未来 7 天股票的价格是
[100,80,60,70,60,75,85]
,那么股票跨度将是[1,1,1,2,1,4,6]
。
实现 StockSpanner
类:
StockSpanner()
初始化类对象。int next(int price)
给出今天的股价price
,返回该股票当日价格的 跨度 。
示例:
输入: ["StockSpanner", "next", "next", "next", "next", "next", "next", "next"] [[], [100], [80], [60], [70], [60], [75], [85]] 输出: [null, 1, 1, 1, 2, 1, 4, 6] 解释: StockSpanner stockSpanner = new StockSpanner(); stockSpanner.next(100); // 返回 1 stockSpanner.next(80); // 返回 1 stockSpanner.next(60); // 返回 1 stockSpanner.next(70); // 返回 2 stockSpanner.next(60); // 返回 1 stockSpanner.next(75); // 返回 4 ,因为截至今天的最后 4 个股价 (包括今天的股价 75) 都小于或等于今天的股价。 stockSpanner.next(85); // 返回 6
提示:
1 <= price <= 105
- 最多调用
next
方法104
次
答案:
class StockSpanner:
def __init__(self):
self.stack = [(float('inf'), 0)]
self.count = 0
def next(self, price):
"""
:type price: int
:rtype: int
"""
self.count += 1
while self.stack and self.stack[-1][0] <= price:
self.stack.pop()
res = self.count - self.stack[-1][1]
self.stack.append((price, self.count))
return res
剑指 Offer 33. 二叉搜索树的后序遍历序列
题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true
,否则返回 false
。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5 / \ 2 6 / \ 1 3
示例 1:
输入: [1,6,3,2,5] 输出: false
示例 2:
输入: [1,3,2,6,5] 输出: true
提示:
数组长度 <= 1000
答案:
class Solution(object):
def verifyPostorder(self, postorder):
"""
:type postorder: List[int]
:rtype: bool
"""
stack = []
length = len(postorder)
root = 99999999
for i in range(length-1,-1,-1):
if root < postorder[i]: #根节点左边所有值都应该小于根节点,若大于返回错误
return False
while stack and stack[-1] > postorder[i]: #递增 #根节点右边所有值都大于根节点
root = stack.pop() #找到根节点
stack.append(postorder[i])
return True
解析:
首先要掌握二叉树以及其遍历的规律方法
之后,我们可以发现根节点左面都小于根节点的值;根节点的右面都大于根节点的值
由此,我们只需要每次找到根节点就好啦~
利用递归栈,可以是一个递增的递归栈(也就是从右向左找到第一个小于的值),此时栈顶元素一定是大于左侧元素的,出栈就为根节点了(可以理解为一个左小右大的分界)。
此时,判断如果取出的根root和左边元素依次对比,若存在小于左边元素的就说明不是搜索二叉树。
其他题目未完待续。。。个人愚见,还望大家指正!!!!谢谢~