數(shù)據(jù)結構之棧

一:概述

????棧的概念其實很好理解滞磺,棧是一種用于存儲數(shù)據(jù)的簡單數(shù)據(jù)結構叹誉,有點類似鏈表或者順序表(統(tǒng)稱線性表),棧與線性表的最大區(qū)別是數(shù)據(jù)的存取的操作蒸苇,我們可以這樣認為棧(Stack)是一種特殊的線性表陡鹃,其插入和刪除操作只允許在線性表的一端進行烘浦,一般而言,把允許操作的一端稱為棧頂(Top)萍鲸,不可操作的一端稱為棧底(Bottom)闷叉,同時把插入元素的操作稱為入棧(Push),刪除元素的操作稱為出棧(Pop)。若棧中沒有任何元素脊阴,則稱為空棧握侧,棧的結構可以通過下圖形象的表示:
棧.004.jpeg

????由圖我們可看成棧只能從棧頂存取元素,同時先進入的元素反而是后出嘿期,而棧頂永遠指向棧內最頂部的元素品擎。到此可以給出棧的正式定義:棧(Stack)是一種有序特殊的線性表,只能在表的一端(稱為棧頂备徐,top萄传,總是指向棧頂元素)執(zhí)行插入和刪除操作,最后插入的元素將第一個被刪除蜜猾,因此棧也稱為后進先出(Last In First Out,LIFO)或先進后出(First In Last Out FILO)的線性表秀菱。棧的基本操作創(chuàng)建棧振诬,判空,入棧衍菱,出棧赶么,獲取棧頂元素等,注意棧不支持對指定位置進行刪除梦碗,插入禽绪,其接口Stack聲明如下:

public interface Stack<T> {
    // 棧是否為空
    boolean isEmpty();

    // 入棧
    void push(T data);

    // 取出棧頂元素蓖救,不出棧
    T peek();

    // 出棧
    T pop();
}

二:順序棧的設計與實現(xiàn)
????順序棧洪规,顧名思義就是采用順序表實現(xiàn)的的棧,順序棧的內部以順序表為基礎循捺,實現(xiàn)對元素的存取操作斩例,當然我們還可以采用內部數(shù)組實現(xiàn)順序棧,在這里我們使用內部數(shù)據(jù)組來實現(xiàn)棧:

public class SeqStack<T> implements Stack<T>{
    //棧頂索引从橘,-1代表空棧
    private int top = -1;

    //默認容量大小
    private int capacity = 10;

    //存放數(shù)據(jù)的數(shù)組
    private T[] arr;

    //棧的實際使用量
    private int size;

    public SeqStack() {
        arr = (T[]) new Object[capacity];
    }

    //是否是空棧的判斷依據(jù)是top是否為-1;如果大于等于0念赶,說明棧中有元素
    @Override
    public boolean isEmpty() {
        return top == -1;
    }

    //獲取棧頂元素,本例基于數(shù)組來實現(xiàn)棧恰力,所以棧頂元素叉谜,就是數(shù)組的最后一個元素
    @Override
    public T peek() {
        //如果是空棧就拋出異常
        if(isEmpty()) {
            new EmptyStackException();
        }
        //返回數(shù)組的最后一個元素
        return arr[top];
    }

    //獲取棧頂元素并出棧
    @Override
    public T pop() {
        //國際慣例,空棧拋出異常
        if(isEmpty()) {
            new EmptyStackException();
        }
        size --;
        //需要注意的是踩萎,這里不會將數(shù)組的最后一個元素置空停局,因為top的值已經修改
        //下次再push的時候只是把原來的數(shù)組元素的值進行了修改,因為一旦pop后香府,
        //top的值減小了董栽,原來的數(shù)組的元素永遠訪問不到,完全沒有必要置空.
        return arr[top--];
    }

    //入棧
    @Override
    public void push(T data) {
        //如果棧已滿企孩,那么擴容
        if(size == arr.length) {
            ensureCapacity(size*2+1);
        }
        //先將棧頂指針+1锭碳,然后將元素放入top + 1那個位置
        arr[++top] = data;
        size++;
    }

    //擴容的思路很簡單,創(chuàng)建一個新的數(shù)組勿璃,然后將原來數(shù)組的數(shù)據(jù)復制到新數(shù)組
    private void ensureCapacity(int capacity) {
        //以防萬一
        if(capacity > size) {
            return;
        }
        
        T[] old = arr;
        arr = (T[]) new Object[capacity];
        for(int i = 0 ; i < old.length ; i++) {
            arr[i] = old[i];
        }
    }
}

????測試代碼:

public static void main(String[] args) {
    SeqStack<String> stack = new SeqStack<>();
    stack.push("A");
    stack.push("B");
    stack.push("C");
    stack.push("D");

    int l = stack.size;
    System.out.println("size : " + l + " , top index : " + stack.top + 
        " , top : " + stack.peek());
        
    for(int i = 0 ; i < l; i ++) {
        stack.pop();
    }
    //此處拋出異常
    System.out.println("size : " + l + " , top : " + stack.peek());
}

????測試結果:

push A , size : 1
push B , size : 2
push C , size : 3
push D , size : 4
size : 4 , top index : 3 , top : D
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
    at teststack.SeqStack.peek(SeqStack.java:58)
    at teststack.SeqStack.main(SeqStack.java:42)

????從上面可以看出擒抛,順序棧的邏輯很簡單,就是操作底層的數(shù)組而已补疑,所有的入棧和出棧操作歧沪,都是往數(shù)組添加和刪除元素,然后改變top指針的位置癣丧。
????以上就是順序棧的實現(xiàn)槽畔,簡單。

三:鏈式棧的設計與實現(xiàn)

????了解完順序棧胁编,我們接著來看看鏈式棧厢钧,所謂的鏈式棧(Linked Stack)鳞尔,就是采用鏈式存儲結構的棧,由于我們操作的是棧頂一端早直,因此這里采用單鏈表作為基礎寥假,直接實現(xiàn)棧的添加,獲取霞扬,刪除等主要操作即可糕韧。其操作過程如下圖:
5.005.jpeg
????從上圖可以看出,鏈式棧的設計就是頭節(jié)點就是棧頂元素喻圃,所有的入棧和出棧萤彩,都是操作頭結點,下面用代碼來實現(xiàn):
public class LinkStack<T> implements Stack<T>{
    //棧頂元素
    private Node<T> top;

    private int size;

    public LinkStack() {
        top = new Node<T>();
    }

    //如果棧頂元素為空斧拍,或者棧頂元素的數(shù)據(jù)為空雀扶,一律視為空棧
    @Override
    public boolean isEmpty() {
        return top == null || top.data == null;
    }

    @Override
    public void push(T data) {
        if (data == null) {
            try {
                throw new Exception("data can\'t be null");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //如果棧頂節(jié)點是空的話,那么創(chuàng)建一個新的節(jié)點當做棧頂節(jié)點
        if (top == null) {
            top = new Node<>(data);
        //如果棧頂節(jié)點的數(shù)據(jù)為空的話肆汹,那么將數(shù)據(jù)賦值給該節(jié)點
        } else if (top.data == null) {
            top.data = data;
        //如果棧頂節(jié)點和數(shù)據(jù)都不為空愚墓,說明不是空棧,這時候需要創(chuàng)建一個
        //新的節(jié)點昂勉,將它賦值給棧頂節(jié)點浪册,同時將它的next指向當前的棧頂節(jié)點
        } else {
            Node<T> p = new Node<T>(data, this.top);
            top = p;
        }
        size++;
    }

    // 獲取棧頂元素,我們一直持有棧頂節(jié)點的引用岗照,直接返回好了
    @Override
    public T peek() {
        if (isEmpty()) {
            new EmptyStackException();
        }
        return top.data;
    }

    //出棧村象,就是將棧頂節(jié)點的下一個節(jié)點當做新的棧頂節(jié)點,
    //實質上就是移除鏈表中的頭結點谴返,很簡單
    @Override
    public T pop() {
        if (isEmpty()) {
            new EmptyStackException();
        }

        T data = top.data;
        top = top.next;
        size--;
        return data;
    }

    //節(jié)點
    class Node<T> {
        public T data;
        public Node<T> next;

        public Node() {
        }

        public Node(T data) {
            this.data = data;
        }

        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
}

????測試代碼:

public static void main(String[] args) {
    LinkStack<String> sl = new LinkStack<>();
    sl.push("A");
    sl.push("B");
    sl.push("C");
    int length = sl.size;
    for (int i = 0; i < length; i++) {
        System.out.println("sl.pop->" + sl.pop() + " , size : " + sl.size);
    }
    sl.push("T");
    System.out.println("sl.pop->" + sl.peek() + " , size : " + sl.size);
}

????測試結果:

sl.pop->C , size : 2
sl.pop->B , size : 1
sl.pop->A , size : 0
sl.pop->T , size : 1

????綜上可知煞肾,棧的實現(xiàn)還是很簡單的,下面講下棧的應用場景嗓袱。
四:棧的應用
????棧是一種很重要的數(shù)據(jù)結構籍救,在計算機中有著很廣泛的應用,如下一些操作都應用到了棧:
????符號匹配
????中綴表達式轉換為后綴表達式
????計算后綴表達式
????實現(xiàn)函數(shù)的嵌套調用
????HTML和XML文件中的標簽匹配
????網頁瀏覽器中已訪問頁面的歷史記錄

????符號匹配:
????在編寫程序的過程中渠抹,我們經常會遇到諸如圓括號“()”與花括號“{}”蝙昙,這些符號都必須是左右匹配的,這就是我們所說的符合匹配類型梧却,當然符合不僅需要個數(shù)相等奇颠,而且需要先左后右的依次出現(xiàn),否則就不符合匹配規(guī)則放航,如“)(”烈拒,明顯是錯誤的匹配,而“()”才是正確的匹配。有時候符合如括號還會嵌套出現(xiàn)荆几,如“9-(5+(5+1))”,而嵌套的匹配原則是一個右括號與其前面最近的一個括號匹配吓妆,事實上編譯器幫我檢查語法錯誤是也是執(zhí)行一樣的匹配原理,而這一系列操作都需要借助棧來完成吨铸,接下來我們使用棧來實現(xiàn)括號”()”是否匹配的檢測行拢。
????判斷原則如下(str=”((5-3)*8-2)”):
????a.設置str是一個表達式字符串,從左到右依次對字符串str中的每個字符char進行語法檢測诞吱,如果char是舟奠,左括號則入棧,如果char是右括號則出棧(有一對匹配就可以去匹配一個左括號房维,因此可以出棧)沼瘫,若此時出棧的字符char為左括號,則說明這一對括號匹配正常握巢,如果此時棧為空或者出棧字符不為左括號晕鹊,則表示缺少與char匹配的左括號松却,即目前不完整暴浦。
????b.重復執(zhí)行a操作,直到str檢測結束晓锻,如果此時棧為空歌焦,則全部括號匹配,如果棧中還有左括號砚哆,是說明缺少右括號独撇。
????接著我們用棧作為存儲容器通過代碼來實現(xiàn)這個過程

public class CheckExpression {
 
    public static String isValid(String expstr){
        //創(chuàng)建棧
        LinkedStack<String> stack = new LinkedStack<>();
        int i = 0 ;
        //遍歷字符串,挨個取出每個字符
        while(i < expstr.lenght()){
            char c = expstr.charAt(i);
            i++;
            switch (ch){
                //如果該字符是左括號躁锁,那么放入棧中
                case '(' :
                    stack.push(ch + "");
                    break;
                //如果是右括號纷铣,那么將左括號出棧
                case ")" :
                     if(stack.isEmpty() || !stack.pop().equals("(")) {
                         return "FAILURE";
                     }
             }    
        }
        //經過這么一輪循環(huán),如果棧為空战转,說明每個左括號都有
        //右括號和他匹配搜立,這時候校驗通過,否則不通過
        if(stack.isEmpty()) {
            return "PASS";
        }else {
            return "FAILURE";
        }
    }
}

????測試代碼:

String expstr="((5-3)*8-2)";
System.out.println(expstr + " check  " + check(expstr));        
String expstr1="((5-3)*8-2";
System.out.println(expstr1 + " check  " + check(expstr1));

????測試結果:

((5-3)*8-2) check  PASS
((5-3)*8-2 check  FAILURE

摘自:https://blog.csdn.net/javazejian/article/details/53362993

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末槐秧,一起剝皮案震驚了整個濱河市啄踊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刁标,老刑警劉巖颠通,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異膀懈,居然都是意外死亡顿锰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硼控,“玉大人乘客,你說我怎么就攤上這事〉硇” “怎么了易核?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浪默。 經常有香客問我牡直,道長,這世上最難降的妖魔是什么纳决? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任碰逸,我火速辦了婚禮,結果婚禮上阔加,老公的妹妹穿的比我還像新娘饵史。我一直安慰自己,他們只是感情好胜榔,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布胳喷。 她就那樣靜靜地躺著,像睡著了一般夭织。 火紅的嫁衣襯著肌膚如雪吭露。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天尊惰,我揣著相機與錄音讲竿,去河邊找鬼。 笑死弄屡,一個胖子當著我的面吹牛题禀,可吹牛的內容都是我干的。 我是一名探鬼主播膀捷,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼迈嘹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了担孔?” 一聲冷哼從身側響起江锨,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糕篇,沒想到半個月后啄育,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡拌消,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年挑豌,在試婚紗的時候發(fā)現(xiàn)自己被綠了安券。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡氓英,死狀恐怖侯勉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情铝阐,我是刑警寧澤址貌,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站徘键,受9級特大地震影響练对,放射性物質發(fā)生泄漏。R本人自食惡果不足惜吹害,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一螟凭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧它呀,春花似錦螺男、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至政恍,卻和暖如春汪拥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背篙耗。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宪赶,地道東北人宗弯。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像搂妻,于是被迫代替她去往敵國和親蒙保。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內容