leetcode - 單調(diào)棧

496. 下一個更大元素 I

題目描述

給定兩個沒有重復(fù)元素 的數(shù)組 nums1 和 nums2 乞榨,其中nums1 是 nums2 的子集。
找到 nums1 中每個元素在 nums2 中的下一個比其大的值。 
nums1 中數(shù)字 x 的下一個更大元素是指 x 在 nums2 中對應(yīng)位置的右邊的第一個
比 x 大的元素澈蟆。如果不存在吩蔑,對應(yīng)位置輸出 -1 昔期。 

示例 1: 
輸入: nums1 = [4,1,2], nums2 = [1,3,4,2].
輸出: [-1,3,-1]
解釋:
對于num1中的數(shù)字4犁功,你無法在第二個數(shù)組中找到下一個更大的數(shù)字趣避,因此輸出 -1晚顷。
對于num1中的數(shù)字1峰伙,第二個數(shù)組中數(shù)字1右邊的下一個較大數(shù)字是 3。
對于num1中的數(shù)字2该默,第二個數(shù)組中沒有下一個更大的數(shù)字瞳氓,因此輸出 -1。 

示例 2: 
輸入: nums1 = [2,4], nums2 = [1,2,3,4].
輸出: [3,-1]
解釋:
對于 num1 中的數(shù)字 2 栓袖,第二個數(shù)組中的下一個較大數(shù)字是 3 匣摘。
對于 num1 中的數(shù)字 4 店诗,第二個數(shù)組中沒有下一個更大的數(shù)字,因此輸出 -1 音榜。

提示: 
nums1和nums2中所有元素是唯一的庞瘸。 
nums1和nums2 的數(shù)組大小都不超過1000。 

Related Topics 棧 

暴力解法最容易想到囊咏,而且相對來說理解簡單:

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 暴力解法
        res = []
        for num1 in nums1:
            for i in range(nums2.index(num1)+1, len(nums2)):
                if nums2[i] > num1:
                    res.append(nums2[i])
                    break
            else:
                res.append(-1)
        return res

其時間復(fù)雜度為 O(n^2)恕洲,而優(yōu)化方法便是單調(diào)棧。若把數(shù)組看成不同高度的人的排隊結(jié)果梅割,每個元素的下一個更大的元素便是他回頭看到的第一個高個子霜第。中間的矮個兒即可忽略(也即被擋住)户辞。
解題中需要注意的是入棧順序是倒敘泌类。因此下一個元素入棧前比其小的元素均要出棧,因為這些元素均是在這個元素的后面底燎,這個元素入棧后比其小的元素均被其擋住刃榨。

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        hashmap = {}
        stack = []
        for i in range(len(nums2)-1, -1, -1):
            while stack and stack[-1] < nums2[i]:
                stack.pop()
            hashmap[nums2[i]] = -1 if not stack else stack[-1]
            stack.append(nums2[i])
        # print(hashmap)
        res = []
        for num in nums1:
            res.append(hashmap[num])
        return res

單調(diào)棧解法時間復(fù)雜度為 O(n),因為對于每個元素來說双仍,最多只有入棧和出棧兩個操作枢希。

503. 下一個更大元素 II

題目描述

給定一個循環(huán)數(shù)組(最后一個元素的下一個元素是數(shù)組的第一個元素),輸出每個元素的下一個更大元素朱沃。
數(shù)字 x 的下一個更大的元素是按數(shù)組遍歷順序苞轿,這個數(shù)字之后的第一個比它更大的數(shù),這意味著你應(yīng)該循
環(huán)地搜索它的下一個更大的數(shù)逗物。如果不存在搬卒,則輸出 -1。 

示例 1: 
輸入: [1,2,1]
輸出: [2,-1,2]
解釋: 第一個 1 的下一個更大的數(shù)是 2翎卓;
數(shù)字 2 找不到下一個更大的數(shù)契邀; 
第二個 1 的下一個最大的數(shù)需要循環(huán)搜索,結(jié)果也是 2失暴。

注意: 輸入數(shù)組的長度不會超過 10000坯门。 
Related Topics 棧 

對于循環(huán)數(shù)組元素的下一個元素就不僅僅是其右邊的元素了,也可能是其左邊的元素逗扒。為了實現(xiàn)能同時搜索其左右元素古戴,我們可以將原數(shù)組復(fù)制一份加入到原數(shù)組的后面,然后按照 496. 下一個更大元素 I 的解法:

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        # 利用 496.下一個更大元素I 中的解法缴阎,將數(shù)組復(fù)制一份加入到原數(shù)組后面
        stack = []
        nums = nums + nums
        n = len(nums)
        res = [-1] * n
        for i in range(n-1, -1, -1):
            while stack and stack[-1] <= nums[i]:
                stack.pop()
            res[i] = -1 if not stack else stack[-1]
            stack.append(nums[i])
        # print(res)
        return res[:n//2]

在實際中面對循環(huán)數(shù)組允瞧,一般是采用取余 (%) 來實現(xiàn)。所以本題中我們也可以不復(fù)制數(shù)組,而采用取余來實現(xiàn):

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        stack = []
        n = len(nums)
        res = [-1] * n
        for i in range(2*n-1, -1, -1):
            while stack and stack[-1] <= nums[i % n]:
                stack.pop()
            res[i % n] = -1 if not stack else stack[-1]
            stack.append(nums[i % n])
        # print(res)
        return res

402. 移掉K位數(shù)字

給定一個以字符串表示的非負(fù)整數(shù) num述暂,移除這個數(shù)中的 k 位數(shù)字痹升,使得剩下的數(shù)字最小。 

注意: 
num 的長度小于 10002 且 ≥ k畦韭。 
num 不會包含任何前導(dǎo)零疼蛾。 

示例 1 : 
輸入: num = "1432219", k = 3
輸出: "1219"
解釋: 移除掉三個數(shù)字 4, 3, 和 2 形成一個新的最小的數(shù)字 1219。
 
示例 2 : 
輸入: num = "10200", k = 1
輸出: "200"
解釋: 移掉首位的 1 剩下的數(shù)字為 200. 注意輸出不能有任何前導(dǎo)零艺配。
 
示例 3 : 
輸入: num = "10", k = 2
輸出: "0"
解釋: 從原數(shù)字移除所有的數(shù)字察郁,剩余為空就是0。
 
Related Topics 棧 貪心算法 

題目分析
首先我們需要確定什么樣的數(shù)字應(yīng)該被移除转唉。當(dāng)我們考察位置 i 的數(shù)字時皮钠,若其前面有數(shù)字,如果移除前面的數(shù)字能獲得更小的結(jié)果赠法,那前面的數(shù)字應(yīng)該被移除麦轰。由于每次對比都是臨近的數(shù)字,則考慮使用棧來解決問題砖织。

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        n = len(num)
        if n == k:
            return '0'
        stack = []
        for c in num:
            while stack and stack[-1] > c and k > 0:
                stack.pop()
                k -= 1
            if not stack and c == '0':
                continue
            stack.append(c)
        # 還有要刪除的元素款侵,就從棧頂刪除
        for _ in range(k):
            stack.pop()
        return '0' if not stack else ''.join(stack)

1081. 不同字符的最小子序列

題目描述

返回字符串 text 中按字典序排列最小的子序列,該子序列包含 text 中所有不同字符一次侧纯。 

示例 1: 
輸入:"cdadabcc"
輸出:"adbc"

示例 2: 
輸入:"abcd"
輸出:"abcd"
 
示例 3: 
輸入:"ecbacba"
輸出:"eacb"

示例 4: 
輸入:"leetcode"
輸出:"letcod"

提示: 
1 <= text.length <= 1000 
text 由小寫英文字母組成 

注意:本題目與 316 https://leetcode-cn.com/problems/remove-duplicate-letters/ 相同 
Related Topics 棧 貪心算法 字符串 

題目分析
本題需要考慮的重點是怎么確定該字符是否需要刪除新锈。如果后面還有該字符,則前面比當(dāng)前大的字符應(yīng)該被刪除眶熬,以保留字典序最小的結(jié)果妹笆。鑒于此,我們考慮對所有字符進(jìn)行計數(shù)聋涨,用于判斷后面是否還有該字符晾浴。

class Solution:
    def smallestSubsequence(self, s: str) -> str:
        # 計數(shù)
        hashmap = {}
        for c in s:
            hashmap.setdefault(c, 0)
            hashmap[c] += 1
        stack = []
        inStack = {}
        for c in s:
            hashmap[c] -= 1
            if inStack.get(c):
                continue
            while stack and stack[-1] > c:
                if hashmap[stack[-1]] == 0:
                    break
                inStack[stack.pop()] = False
            stack.append(c)
            inStack[c] = True
        return ''.join(stack)

321. 拼接最大數(shù)

題目描述

給定長度分別為 m 和 n 的兩個數(shù)組负乡,其元素由 0-9 構(gòu)成牍白,表示兩個自然數(shù)各位上的數(shù)字。
現(xiàn)在從這兩個數(shù)組中選出 k (k <= m + n) 個數(shù)字拼接成一個新的數(shù)抖棘,要求從同一個數(shù)組中
取出的數(shù)字保持其在原數(shù)組中的相對順序茂腥。 

求滿足該條件的最大數(shù)。結(jié)果返回一個表示該最大數(shù)的長度為 k 的數(shù)組切省。 
說明: 請盡可能地優(yōu)化你算法的時間和空間復(fù)雜度最岗。 

示例 1: 
輸入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
輸出:
[9, 8, 6, 5, 3] 

示例 2: 
輸入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
輸出:
[6, 7, 6, 0, 4] 

示例 3: 
輸入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
輸出:
[9, 8, 9] 
Related Topics 貪心算法 動態(tài)規(guī)劃 

本題留著挑戰(zhàn)吧。

參考

  1. 單調(diào)棧解決 Next Greater Number 一類問題
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朝捆,一起剝皮案震驚了整個濱河市般渡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖驯用,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脸秽,死亡現(xiàn)場離奇詭異,居然都是意外死亡蝴乔,警方通過查閱死者的電腦和手機(jī)记餐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薇正,“玉大人片酝,你說我怎么就攤上這事⊥谘” “怎么了雕沿?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猴仑。 經(jīng)常有香客問我晦炊,道長,這世上最難降的妖魔是什么宁脊? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任断国,我火速辦了婚禮,結(jié)果婚禮上榆苞,老公的妹妹穿的比我還像新娘稳衬。我一直安慰自己,他們只是感情好坐漏,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布薄疚。 她就那樣靜靜地躺著,像睡著了一般赊琳。 火紅的嫁衣襯著肌膚如雪街夭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天躏筏,我揣著相機(jī)與錄音板丽,去河邊找鬼。 笑死趁尼,一個胖子當(dāng)著我的面吹牛埃碱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酥泞,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼砚殿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芝囤?” 一聲冷哼從身側(cè)響起似炎,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤辛萍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后羡藐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叹阔,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年传睹,在試婚紗的時候發(fā)現(xiàn)自己被綠了耳幢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡欧啤,死狀恐怖睛藻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邢隧,我是刑警寧澤店印,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站倒慧,受9級特大地震影響按摘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纫谅,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一炫贤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧付秕,春花似錦兰珍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猛计,卻和暖如春唠摹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奉瘤。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工勾拉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毛好。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓望艺,卻偏偏與公主長得像苛秕,于是被迫代替她去往敵國和親肌访。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353