棧:
典型的題目有括號匹配,遞歸時用到棧,最小棧,由內到外處理,維持最大最小,這種都用到棧。做棧類的題
- 首先想好棧元素的意義
- 入棧
- 出棧
做題順序: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】