栈:
典型的题目有括号匹配,递归时用到栈,最小栈,由内到外处理,维持最大最小,这种都用到栈。做栈类的题
- 首先想好栈元素的意义
- 入栈
- 出栈
做题顺序:20、42、496、503、739、901、84、71、85、144、94、145
其中【42、496、503、739】维护一个单调递减的栈
【leetcode20】
题目:给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
思路详解:给定字符串s,从左到右依次对字符判断,如果字符为开括号(左括号),放入栈中;如果字符为闭括号,判断是否与栈顶元素括号类型匹配,如果不匹配返回false,如果不存在栈顶元素也返回false,知道所有字符判断完成,返回若栈为空则为True。
总结一句话就是左括号入栈,若有右括号和左括号匹配,则左括号出栈,最后判断栈是否为空,为空为True,不空为False.
并且对mapping字典来存储左右括号的使用非常巧妙和高效。
代码如下:
class Solution:
def isValid(self, s: str) -> bool:
stack = [] #用于存放开括号
mapping = {')':'(', '}':'{', ']':'['} #散列表用于匹配括号
for char in s:
if char in mapping:
if len(stack) != 0:
tmp = stack.pop()
if mapping[char] != tmp:
return False
else:
return False
else:
stack.append(char)
return not stack
【leetcode42】
【参考leetcode官解和https://blog.csdn.net/qq_17550379/article/details/84945427这篇博文】
思路:主要是应用木桶原理求木桶中水的含量,即首先知道左右边界的值,找到左右边界的最小值,再用最小值减去中间的值,即为水桶里水的含量。明白这个道理后,怎么样才能找到左右边界的最小值呢?如图,中间有个峰值Peak,以峰值为中届,左面的最小值由左界决定,右面的最小值由右界决定。设置两个指针left 和 right,left = right停止循环此时在峰值。因此,当height[left]<height[right]时left+=1,当height[left]>=height[right]时,right+=1。而左界和右界值分别用max_left和max_right决定 ——>peak<——,就把问题解决出来了。
难点:在于对木桶原理不够了解。
class Solution:
def trap(self, height: List[int]) -> int:
if len(height) < 3:
return 0
hei_len = len(height)
left = 0
right = hei_len - 1
max_left = height[0]
max_right = height[-1]
res = 0
while left < right:
if height[left] < height[right]: #注意理解这个条件,当左边低于右边时,从左边开始处理,当左边大于右边时,从右边开始处理。即永远处理较低一侧!
if max_left < height[left]:
max_left = height[left]
else:
res += max_left - height[left]
left += 1 #这个是有条件的
else:
if max_right < height[right]:
max_right = height[right]
else:
res += max_right - height[right]
right -= 1
return res
【Leetcode496】下一个更大元素 I
题目:
给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。
思路:法一:最容易想到的是暴力法,即将nums1循环,分别去nums2中寻找大于nums1元素的第一个值。
法二:利用栈和字典,利用单调栈找到下一个更大的元素,然后用字典的形式{当前栈顶元素,下一个更大的元素},最后遍历nums1,键保存在字典中的,返回值,如果没有值即为-1. return [dic.get(i, -1) for i in nums1].这个操作非常好!
为什么会想到用字典呢?????因为nums1是nums2的子集,且nums2中没有重复元素。
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
stack = []
dic = {}
for i in nums2:
while stack and stack[-1] < i:
dic[stack.pop()] = i
stack.append(i)
return [dic.get(i, -1) for i in nums1]
【Leetcode503】下一个更大元素 II
题目:
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
注意: 输入数组的长度不会超过 10000。
思路:对于循环来说,可以将两个相同列表合并进行上面的处理,这样【1,2,1】就能找到比它们大的元素值。
上一题因为是两个列表,因此用字典进行映射;这一题是一个列表,因此我们将值存在列表里就可以,不需要映射。
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
nums1 = nums + nums
res = [-1] * len(nums1) #这样才可以进行res[stack.pop()] = nums1[i]操作
# tar = nums[0]
stack = []
for i in range(len(nums1)):
while stack and nums1[stack[-1]] < nums1[i]:
res[stack.pop()] = nums1[i]
stack.append(i)
return res[:len(nums)]
总结:这道题思路很容易想出来,但是实现时还要注意一些细节,比如对res列表对应索引的赋值先创建一个都是-1的列表。
题目:
根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
思路:这道题基本和前一道题一样。用列表来存储结果,也是采用单调栈【下一个更大元素】,注意res一开始初始化成res=[0]*len(T)
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]:
j = stack.pop()
res[j] = i - j
stack.append(i)
return res
题目:
编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。
示例:
输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1,
S.next(80) 被调用并返回 1,
S.next(60) 被调用并返回 1,
S.next(70) 被调用并返回 2,
S.next(60) 被调用并返回 1,
S.next(75) 被调用并返回 4,
S.next(85) 被调用并返回 6。
注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。
思路:这属于单调栈问题,入栈元素有次序且找连续比price值小的数的个数,仍然套用单调栈的模板
#基本模板如下
stack = []
while stack and stack[-1] <= price:
stack.pop()
stack.append(price)
难点:1、没看懂输入输出规则
2、为栈中元素编号,相当于一条入栈信息为(price,count)
总结:理解题意,判断是采用单调栈方法,套用模板,再对具体问题具体分析完善代码。
【leetcode84】柱状图中最大的矩形
题目:
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]
。
示例:
输入: [2,1,5,6,2,3]
输出: 10
思路:求出每个元素[i]为最矮柱子能够延伸的最大距离。利用递增栈的方法。详情见代码。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
stack = [-1] #为了使得heights的第一个元素满足res = max(res, heights[tmp]*(i-stack[-1]-1))公式
res = 0
heights.append(-1) #为了使得heights中所有元素都有机会出栈计算res
l = len(heights)
for i in range(l):
while heights[stack[-1]] > heights[i]:
tmp = stack.pop()
res = max(res, heights[tmp]*(i-stack[-1]-1)) #计算递增序列里,计算tmp相邻右边有没有比它还大的即(i-stack[-1]-1)
#“以第i根柱子为最矮柱子所能延伸的最大面积”估计会好理解点。
stack.append(i)
return res
【leetcode71】简化路径
题目:
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。
输入:"/a/./b/../../c/"
输出:"/c"
思路:我的体会是题目中要求按照一定的顺序,就能用到栈
难点:对于我来说难点就是字符串的操作上面,如字符串的分隔str.split('/')和字符串的连接str.join('/')【因为拿到这道题以后完全不知道'/'该如何处理】
代码如下:
class Solution:
def simplifyPath(self, path: str) -> str:
stack = []
path = path.split("/")
for item in path:
if item == "..":
if stack : stack.pop()
elif item and item != ".":
stack.append(item)
return "/" + "/".join(stack)
【leetcode85】最大矩形
题目:
给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
示例:
输入:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
输出: 6
思路:和leetcode84题类似,将这个题目转化成求柱状图的最大矩形,因为组成长方形需要连续的‘1’,因此,我们可以将将这个矩阵看成多个柱状图,第一行、前两行、前三行等,分别计算出来柱状图的最大矩形,再选择最终最大的矩形。
代码如下:
class Solution:
# def maximalRectangle(self, matrix: List[List[str]]) -> int:
def largestRectangleArea(self, heights):
stack = [-1]
res = 0
heights.append(-1)
for idx, val in enumerate(heights):
while heights[stack[-1]] > val:
tmp = stack.pop()
res = max(res, heights[tmp]*(idx-stack[-1]-1))
stack.append(idx)
return res
def maximalRectangle(self, matrix):
if len(matrix) == 0 or len(matrix[0]) == 0:
return 0
pz = [0] * len(matrix[0])
max_m = 0
for i in range(len(matrix)):
for j in range(len(matrix[0])):
pz[j] = pz[j] + 1 if matrix[i][j] == '1' else 0
max_m = max(max_m, self.largestRectangleArea(pz))
return max_m
【二叉树的前序、中序、后序遍历请详见https://blog.csdn.net/weixin_42723548/article/details/101063955】