leetcode單調棧總結

單調棧是在棧的先進後出基礎之上額外添加一個特性:從棧頂到棧底的元素是嚴格遞增(or遞減)動畫:什麼是單調棧?一文很直觀的解釋了單調棧並給出了單調遞減棧的一個例子,但是上文中誤寫爲單調遞增棧。當遇到的問題,和前後元素之間的大小關係有關係時可以考慮使用單調棧。如單調遞增棧可以找到左起第一個比當前數字小的元素,單調遞減棧可以找到左起第一個比當前數字大的元素。

以單調遞減棧爲例,每當我們遇到一個比當前棧頂所對應的數大的數的時候,彈出棧內所有比這個數小的棧內元素,而對於每一個元素,當它出棧的時候,說明它遇到了自己的下一個比他大的值。然後將當前數壓棧。

496.下一個更大元素1

給定兩個沒有重複元素的數組 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每個元素在 nums2 中的下一個比其大的值。

nums1 中數字 x 的下一個更大元素是指 x 在 nums2 中對應位置的右邊的第一個比 x 大的元素。如果不存在,對應位置輸出-1。

輸入: nums1 = [4,1,2], nums2 = [1,3,4,2].
輸出: [-1,3,-1]
解釋:
    對於num1中的數字4,你無法在第二個數組中找到下一個更大的數字,因此輸出 -1。
    對於num1中的數字1,第二個數組中數字1右邊的下一個較大數字是 3。
    對於num1中的數字2,第二個數組中沒有下一個更大的數字,因此輸出 -1。

輸入: nums1 = [2,4], nums2 = [1,2,3,4].
輸出: [3,-1]
解釋:
    對於num1中的數字2,第二個數組中的下一個較大數字是3。
    對於num1中的數字4,第二個數組中沒有下一個更大的數字,因此輸出 -1。

首先以簡單經典問題距離,該問題是單調棧的直接應用。遍歷一次nums2數組,構建單調遞減棧即可知道nums2中所有數字下一個更大的數是什麼,將這個結果保存在字典中,查詢提取即可。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        // 單調遞減棧
        Map<Integer,Integer> map = new HashMap<>();
        Stack<Integer> stack = new Stack<>();
        for(int num:nums2) {
            while(!stack.empty()) {
                if (stack.peek() < num) {
                    map.put(stack.pop(),num);
                } else {
                    break;
                }
            }
            stack.push(num);
        }
        int[] ret = new int[nums1.length];
        for(int i=0;i<nums1.length;i++) {
            if(map.containsKey(nums1[i])) {
                ret[i] = map.get(nums1[i]);
            } else {
                ret[i] = -1;
            }
        }
        return ret;
    }
}

503.下一個更大元素

給定一個循環數組(最後一個元素的下一個元素是數組的第一個元素),輸出每個元素的下一個更大元素。數字 x 的下一個更大的元素是按數組遍歷順序,這個數字之後的第一個比它更大的數,這意味着你應該循環地搜索它的下一個更大的數。如果不存在,則輸出 -1。

輸入: [1,2,1]
輸出: [2,-1,2]
解釋: 第一個 1 的下一個更大的數是 2;
數字 2 找不到下一個更大的數; 
第二個 1 的下一個最大的數需要循環搜索,結果也是 2。

本題與上題相比添加了數組可以循環,並且數組中存在重複數字。遍歷數組兩次即可達到循環的效果,對於重複數字,通過在棧中保存索引來區別是重複數字中的哪一個。

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        // 單調遞減棧,存儲索引(數字存在重複)
        Stack<Integer> stack = new Stack<>();
        int[] ret = new int[nums.length];
        for(int i=0;i<ret.length;i++) {
            ret[i] = -1;
        }
        for(int i=0;i<2*nums.length - 1;i++) {
            while(!stack.empty()) {
                if(nums[i%nums.length] > nums[stack.peek()]) {
                    ret[stack.pop()] = nums[i%nums.length];
                } else {
                    break;
                }
            }
            stack.push(i%nums.length);
        }
        return ret;
    }
}

42.接雨水

給定 n 個非負整數表示每個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。

上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。 感謝 Marcos 貢獻此圖。

示例:

輸入: [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6

當找到比當前值大的下一個值後,這個部分可能可以接到雨水,此時需要比較當前值的左端也就是棧頂和比當前值大的下一個值,看看這兩個值是否構成接雨水的條件,也就是需要在這兩個值中選一個最小值,再減去當前值,就是可以接雨水的高度。寬度使用索引相減即可。

class Solution {
    public int trap(int[] height) {
        // 單調遞減棧存儲索引
        Stack<Integer> stack = new Stack<>();
        int ret = 0;
        for(int i=0;i<height.length;i++) {
            while(!stack.empty() && height[i] > height[stack.peek()]) {
                int index = stack.pop();
                if (stack.empty()) {
                    break;
                }
                //棧頂和當前爲接雨水的兩端,index處爲低谷
                int distance = i - stack.peek() - 1;  
                int curheight = Math.min(height[i],height[stack.peek()]) - height[index];
                ret += curheight * distance;
            }
            stack.push(i);
        }
        return ret;
    }
}

739.每日溫度

根據每日 氣溫 列表,請重新生成一個列表,對應位置的輸入是你需要再等待多久溫度纔會升高超過該日的天數。如果之後都不會升高,請在該位置用 0 來代替。

例如,給定一個列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的輸出應該是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:氣溫 列表長度的範圍是 [1, 30000]。每個氣溫的值的均爲華氏度,都是在 [30, 100] 範圍內的整數。

是一道典型的單調棧問題。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章