遞減棧求解下一個更大元素問題
首先貼出兩道題:
leetcode739
根據每日 氣溫
列表,請重新生成一個列表,對應位置的輸入是你需要再等待多久溫度才會升高的天數。如果之後都不會升高,請輸入 0
來代替。
例如,給定一個列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
,你的輸出應該是 [1, 1, 4, 2, 1, 1, 0, 0]
。
提示:氣溫
列表長度的範圍是 [1, 30000]
。每個氣溫的值的都是 [30, 100]
範圍內的整數。
leetcode496
給定兩個沒有重複元素的陣列 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。
示例 2:
輸入: nums1 = [2,4], nums2= [1,2,3,4]. 輸出: [3,-1] 解釋: 對於num1中的數字2,第二個陣列中的下一個較大數字是3。 對於num1中的數字4,第二個陣列中沒有下一個更大的數字,因此輸出 -1。
這兩道題都可以歸結為:“在陣列array【i】之後並且大於array【i】的第一個元素在哪”的問題。
我在第二題中使用了雙重for迴圈,很幸運是通過的了。
第一題是明顯不通的。(因為這是中等難度的了)
首先我們檢視一個序列:【1,1,1,1,4】
如果用雙重for迴圈,實際上前4次尋找的都是一個元素而已,就是4.
其實增加了不必要迭代。
遞減棧思路是這樣的:
掃描陣列,如果棧是空的那麼壓如一個元素,因為棧裡沒有元素比這個小,所以肯定要放在最底下。
如果棧不是空的並且當前掃描的元素小於(等於)棧頂元素,壓入。
如果棧不是空的並且掃描元素大於棧頂元素,我們要在不斷彈出元素直至棧頂元素大於當前元素,或者空棧把新元素放入。
我們以第一題的示例來演示遞減棧。
首先有一個空棧:【】。
當前元素73:壓入。【(73,0)】其中0是73的下標,為了到時候放入結果做準備。
當前74,彈出73,放入74.【(74,1)】也就是比73大的第一個元素是74,
當前75,彈出74,放入75.【(75,2)】也就是比74大的一個元素是75.
當前71,壓入。【(75,2),(71,3)】
當前69,壓入。【(75,2),(71,3),(69,4)】
當前72,彈出69,彈出71.也就是比69大的下一個是72,比71大的下一個是72.【(75,2),(72,5)】
當前76,彈出72,75,說明比72,75大的均為76.【(76,6)】
當前73,壓入。【(76,6),(73,7)】
棧不是空的,所以剩餘的兩個元素是沒有下一個更大元素的,可以填入響應特殊值。
這種方法在相當於最好情況的雙重for。沒有多餘的比較。
效率極高,接近O(n)。
附送第一題解:
class Solution:
def dailyTemperatures(self, temperatures):
"""
:type temperatures: List[int]
:rtype: List[int]
"""
stk=[]
record=[0 for x in range(len(temperatures))]
for i,t in enumerate(temperatures):
if len(stk)==0:
stk.append((i,t))
else:
if t<stk[-1][1]:
stk.append((i,t))
else:
while len(stk)>0 and stk[-1][1]<t:
index,temp=stk.pop()
record[index]=i-index
stk.append((i,t))
return record