算法 1.3.1 最小棧 【leetcode 155】

題目描述

最小棧
設(shè)計(jì)一個(gè)支持 push ,pop ,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。

push(x) —— 將元素 x 推入棧中。
pop() —— 刪除棧頂?shù)脑亍?br> top() —— 獲取棧頂元素。
getMin() —— 檢索棧中的最小元素。

示例:
輸入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
輸出:
[null,null,null,null,-3,null,0,-2]

解釋:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

數(shù)據(jù)結(jié)構(gòu)

  • 數(shù)組(棧)、鏈表(鏈棧)

算法思維

  • 遍歷、狀態(tài)機(jī)、快照、棧特性(LIFO)

解題要點(diǎn)

  • 使用快照記錄狀態(tài)信息
  • 利用棧的 LIFO 特性,實(shí)現(xiàn)狀態(tài)信息的 更新/回滾

解題思路

一. Comprehend 理解題意
  • 需要自實(shí)現(xiàn)一個(gè)棧結(jié)構(gòu)
  • 除了基礎(chǔ)的入棧、出棧、查看棧頂功能外,還需要實(shí)現(xiàn)一個(gè)查詢最小值的功能
二. Choose 選擇數(shù)據(jù)結(jié)構(gòu)與算法
解法一:使用數(shù)組實(shí)現(xiàn)棧結(jié)構(gòu),棧的最小值通過(guò)遍歷數(shù)組獲得
  • 數(shù)據(jù)結(jié)構(gòu):數(shù)組
  • 算法思維:遍歷
三. Code 編碼實(shí)現(xiàn)基本解法
class MinStack {

        //1.聲明存放數(shù)據(jù)的數(shù)組和棧頂指針
        Integer[] arr;
        int index;

        /**
         * initialize your data structure here.
         */
        public MinStack() {
            //2.構(gòu)造器中進(jìn)行參數(shù)初始化
            arr = new Integer[1000];
            index = -1;
        }

        public void push(int x) {
            //3.入棧 -- 自動(dòng)裝箱
            arr[++index] = x;

            //數(shù)組擴(kuò)容
            if (index == arr.length*0.8){
                Integer[] arr1 = new Integer[arr.length*2];
                for (int i=0; i<arr.length; i++){
                    arr1[i] = arr[i];
                }
                arr = arr1;
            }
        }

        public void pop() {
            if(index < 0) return;
            //4.出棧 -- 相當(dāng)于刪除棧頂元素 -- 棧頂賦值為 null
            arr[index--] = null;
        }

        public int top() {
            if (index < 0) return Integer.parseInt(null);
            //5.返回棧頂元素 -- 自動(dòng)拆箱
            return arr[index];
        }

        public int getMin() {
            if (index < 0) return Integer.parseInt(null);

            //6.遍歷數(shù)組尋找最小值
            int minus = arr[0]; //最小值
            for (int i=1; i<=index; i++){
                minus = arr[i] < minus ? arr[i] : minus;
            }
            return minus;
        }
    }

執(zhí)行耗時(shí):65 ms,擊敗了 7.35% 的Java用戶
內(nèi)存消耗:40.2 MB,擊敗了 55.30% 的Java用戶
時(shí)間復(fù)雜度:O(n) -- 數(shù)組的遍歷 O(n),數(shù)組的擴(kuò)容 O(n)
空間復(fù)雜度:O(n) -- 數(shù)組的內(nèi)存空間 O(n)

四. Consider 思考更優(yōu)解

改變算法思維!

思路:
  • 每次獲取最小值時(shí),都需要重新 遍歷 數(shù)組,效率很低
  • 不難發(fā)現(xiàn),每個(gè)元素在被壓入棧頂?shù)臅r(shí)候,棧的最小值都只有 兩種狀態(tài)
    1.當(dāng)前元素為最小值(狀態(tài)一:某個(gè)新的數(shù)值)
    2.以前的最小值仍為最小值(狀態(tài)二:原數(shù)值)
  • 能否利用棧的特性,彈出棧頂元素的時(shí)候,將最小值的狀態(tài)變化也一并彈出?
方法:
  • 讓每個(gè)元素都攜帶一個(gè)“快照”,記錄此元素入棧后(這一時(shí)刻)棧的最小值。
  • 當(dāng)前棧的最小值 == 當(dāng)前棧頂元素快照中的值
  • 當(dāng)棧頂元素被彈出,上個(gè)元素成為棧頂,上個(gè)快照被啟用,最小值回滾
解法二:使用鏈表實(shí)現(xiàn)棧結(jié)構(gòu),棧的最小值由棧頂元素?cái)y帶
  • 數(shù)據(jù)結(jié)構(gòu):鏈表(鏈棧)
  • 算法思維:狀態(tài)機(jī)、快照、棧特性(LIFO)
五. Code 編碼實(shí)現(xiàn)最優(yōu)解
 class MinStack {

        //1.聲明一個(gè)內(nèi)部類 -- 鏈表節(jié)點(diǎn)
        private class Node{
            int val; //當(dāng)前節(jié)點(diǎn)值
            int min; //最小值快照 -- 當(dāng)前節(jié)點(diǎn)入棧后,棧的最小值

            Node next; //指針

            public Node(){}

            public Node(int val, int min, Node next){
                this.val = val;
                this.min = min;
                this.next = next;
            }
        }

        //2.聲明一個(gè)鏈表頭 -- 棧頂
        Node head;

        /**
         * initialize your data structure here.
         */
        public MinStack() {
            //3.初始化鏈棧
            head = null;
        }

        public void push(int x) {
            //4.節(jié)點(diǎn)入棧時(shí),判斷鏈表長(zhǎng)度
            if (head == null) {
                head = new Node(x,x,null);
            } else {
                //5.每次入棧時(shí),比較最小值,將此刻的最小值存入 new Node().min 中
                head = new Node(x,Math.min(x,head.min),head);
            }
        }

        public void pop() {
            //6.出棧時(shí),直接彈出即可,最小值隨之回滾
            if (head != null) head = head.next;
        }

        public int top() {
            //7.查棧頂
            return head != null ? head.val : Integer.parseInt(null);
        }

        public int getMin() {
            //8.查最小值 -- 其實(shí)就是查當(dāng)前棧頂?shù)淖钚≈悼煺?            return head != null ? head.min : Integer.parseInt(null);
        }
    }

執(zhí)行耗時(shí):6 ms,擊敗了 96.02% 的Java用戶
內(nèi)存消耗:40.3 MB,擊敗了 36.21% 的Java用戶
時(shí)間復(fù)雜度:O(1) -- 棧頂元素的直接查詢 O(1)
空間復(fù)雜度:O(n) -- 鏈棧的內(nèi)存空間 O(n)

六. Change 變形與延伸

=== 待續(xù) ===

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 題目:設(shè)計(jì)一個(gè)支持 push,pop,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。 push(x) -- 將...
    關(guān)山Kwan閱讀 287評(píng)論 0 0
  • 題目 設(shè)計(jì)一個(gè)支持 push,pop,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。 push(x) -- 將...
    就是會(huì)把話說(shuō)反閱讀 182評(píng)論 0 0
  • 查看題目詳情可點(diǎn)擊此處。 題目 設(shè)計(jì)一個(gè)支持 push,pop,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。...
    Cloneable閱讀 260評(píng)論 0 0
  • 題目描述 [最小棧] 設(shè)計(jì)一個(gè)支持 push,pop,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。 push...
    一只可愛(ài)的檸檬樹(shù)閱讀 120評(píng)論 0 1
  • 設(shè)計(jì)一個(gè)支持 push ,pop ,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。 push(x) —— 將元...
    放下梧菲閱讀 173評(píng)論 0 0

友情鏈接更多精彩內(nèi)容