數(shù)據(jù)結(jié)構(gòu)與算法(2)——棧和隊(duì)列

前言:題圖無關(guān)靴患,只是好看芽偏,接下來就來復(fù)習(xí)一下棧和隊(duì)列的相關(guān)知識

前序文章:

什么是棧

棧是一種用于存儲數(shù)據(jù)的簡單數(shù)據(jù)結(jié)構(gòu)(與鏈表類似)恶座。數(shù)據(jù)入棧的次序是棧的關(guān)鍵〔。可以把一桶桶裝的薯片看作是一個棧的例子,當(dāng)薯片做好之后瑟押,它們會依次被添加到桶里,每一片都會是當(dāng)前的最上面一片星掰,而每次我們?nèi)〉臅r候也是取的最上面的那一片多望,規(guī)定你不能破壞桶也不能把底部捅穿,所以第一個放入桶的薯片只能最后一個從桶里取出氢烘;

定義:棧(Stack)是一個有序線性表怀偷,只能在表的一端(稱為棧頂,top)執(zhí)行插入和刪除操作播玖。最后插入的元素將第一個被刪除椎工,所以棧也稱為后進(jìn)先出(Last In First Out,LIFO)或先進(jìn)后出(First In Last Out)線性表蜀踏;

兩個改變棧的操作都有專用名稱维蒙。一個稱為入棧(push),表示在棧中插入一個元素果覆;另一個稱為出棧(pop)颅痊,表示從棧中刪除一個元素。試圖對一個空棧執(zhí)行棧操作稱為下溢(underflow)局待;試圖對一個滿棧執(zhí)行棧操作稱為溢出(overflow)斑响。通常,溢出和下溢均認(rèn)為是異常钳榨;

棧的應(yīng)用

  • 無處不在的Undo操作(撤銷)恋捆;
  • 程序調(diào)用的系統(tǒng)棧;
  • 括號/符號匹配重绷;
  • 等等等等....

棧抽象數(shù)據(jù)類型

下面給出棧抽象數(shù)據(jù)類型中的操作,為了簡單起見膜毁,假設(shè)數(shù)據(jù)類型為整型昭卓;

棧的主要操作

  • void push(int data):將data(數(shù)據(jù))插入棧;
  • int pop():刪除并返回最后一個插入棧的元素瘟滨;

棧的輔助操作

  • int top():返回最后一個插入棧的元素候醒,但不刪除;
  • int size():返回存儲在棧中元素的個數(shù)杂瘸;
  • int isEmpty():判斷棧中是否有元素倒淫;
  • int isStackFull():判斷棧中是否存滿元素;

動態(tài)數(shù)組簡單實(shí)現(xiàn)棧結(jié)構(gòu)

我們結(jié)合之前創(chuàng)建的Array類败玉,我們能夠很好的創(chuàng)建屬于我們自己的動態(tài)數(shù)組實(shí)現(xiàn)的棧結(jié)構(gòu)敌土,對于用戶來說镜硕,我們只需要完成我們的相關(guān)操作,并且知道我能夠不斷地往里添加元素而不出錯就行了返干,所以我們先來定義一個通用的棧接口:

public interface Stack<E> {
    int getSize();
    boolean isEmepty();
    void push(E e);
    E pop();
    E top();
}

然后我們往之前的動態(tài)數(shù)組中添加兩個用戶友好的方法:

public E getLast() {
    return get(size - 1);
}

public E getFirst() {
    return get(0);
}

然后實(shí)現(xiàn)自己的動態(tài)數(shù)組為底層的棧結(jié)構(gòu)就輕松多了:

public class ArrayStack<E> implements Stack<E> {

    Array<E> array;

    public ArrayStack(int capacity) {
        array = new Array<>(capacity);
    }

    public ArrayStack() {
        array = new Array<>();
    }

    @Override
    public int getSize() {
        return array.getSize();
    }

    @Override
    public boolean isEmepty() {
        return array.isEmpty();
    }

    public int getCapacity() {
        return array.getCapacity();
    }

    @Override
    public void push(E e) {
        array.addLast(e);
    }

    @Override
    public E pop() {
        return array.removeLast();
    }

    @Override
    public E top() {
        return array.getLast();
    }

    @Override
    public String toString() {

        StringBuilder res = new StringBuilder();
        res.append("Stack:");
        res.append("[");
        for (int i = 0; i < array.getSize(); i++) {
            res.append(array.get(i));
            if (i != array.getSize() - 1) {
                res.append(",");
            }
        }
        res.append("]");
        return res.toString();
    }
}

簡單復(fù)雜度分析

從代碼中可以看出兴枯,幾乎所有的時間復(fù)雜度都為O(1)級別,比較特別的是push()pop()操作可能涉及到底層數(shù)組的擴(kuò)容或縮容的操作矩欠,所以是均攤下來的復(fù)雜度财剖;


隊(duì)列

什么是隊(duì)列

隊(duì)列是一種用于存儲數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)(與鏈表和棧類似),數(shù)據(jù)到達(dá)的次序是隊(duì)列的關(guān)鍵癌淮;在日常生活中隊(duì)列是指從序列的開始按照順序等待服務(wù)的一隊(duì)人或物躺坟;

定義:隊(duì)列是一種只能在一端插入(隊(duì)尾),在另一端刪除(隊(duì)首)的有序線性表乳蓄。隊(duì)列中第一個插入的元素也是第一個被刪除的元素咪橙,所以隊(duì)列是一種先進(jìn)先出(FIFO,First In First Out)或后進(jìn)后出(LiLO,Last In Last Out)線性表;

與棧類似栓袖,兩個改變隊(duì)列的操作各有專用名稱匣摘;在隊(duì)列中插入一個元素,稱為入隊(duì)(EnQueue)裹刮,從隊(duì)列中刪除一個元素音榜,稱為出隊(duì)(DeQueue);試圖對一個空隊(duì)列執(zhí)行出隊(duì)操作稱為下溢(underflow)捧弃,試圖對一個滿隊(duì)列執(zhí)行入隊(duì)操作稱為溢出(overflow)赠叼;通常認(rèn)為溢出和下溢是異常。

隊(duì)列的一些應(yīng)用舉例

  • 操作系統(tǒng)根據(jù)(具有相同優(yōu)先級的)任務(wù)到達(dá)的順序調(diào)度任務(wù)(例如打印隊(duì)列)违霞;
  • 模擬現(xiàn)實(shí)世界中的隊(duì)列嘴办,如售票柜臺前的隊(duì)伍,或者任何需要先來先服務(wù)的場景买鸽;
  • 多道程序設(shè)計(jì)涧郊;
  • 異步數(shù)據(jù)傳輸(文件輸入輸出、管道眼五、套接字)妆艘;
  • 等等等等...

動態(tài)數(shù)組簡單實(shí)現(xiàn)隊(duì)列結(jié)構(gòu)

我們?nèi)匀欢x一個Queue接口來說明我們隊(duì)列中常用的一些方法:

public interface Queue<E> {
    int getSize();
    boolean isEmpty();
    void enqueue(E e);
    E dequeue();
    E getFront();
}

借由我們之前自己實(shí)現(xiàn)的動態(tài)數(shù)組,那么我們的隊(duì)列就很簡單了:

public class ArrayQueue<E> implements Queue<E> {

    private Array<E> array;

    public ArrayQueue(int capacity){
        array = new Array<>(capacity);
    }

    public ArrayQueue(){
        array = new Array<>();
    }

    @Override
    public int getSize(){
        return array.getSize();
    }

    @Override
    public boolean isEmpty(){
        return array.isEmpty();
    }

    public int getCapacity(){
        return array.getCapacity();
    }

    @Override
    public void enqueue(E e){
        array.addLast(e);
    }

    @Override
    public E dequeue(){
        return array.removeFirst();
    }

    @Override
    public E getFront(){
        return array.getFirst();
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append("Queue: ");
        res.append("front [");
        for(int i = 0 ; i < array.getSize() ; i ++){
            res.append(array.get(i));
            if(i != array.getSize() - 1)
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }
}

簡單的復(fù)雜度分析

  • void enquque(E):O(1)(均攤)
  • E dequeue()O(n)
  • E front():O(1)
  • int getSize():O(1)
  • boolean isEmpty():O(1)

實(shí)現(xiàn)自己的循環(huán)隊(duì)列

循環(huán)隊(duì)列的實(shí)現(xiàn)其實(shí)就是維護(hù)了一個front和一個tail分別指向頭和尾看幼,然后需要特別注意的呢是判定隊(duì)滿和隊(duì)空的條件:

  • 隊(duì)空:front == tail批旺,這沒啥好說的;
  • 隊(duì)滿:tail + 1 == front诵姜,這里其實(shí)是有意浪費(fèi)了一個空間汽煮,不然就判定不了到底是隊(duì)空還是隊(duì)滿了,因?yàn)闂l件都一樣...
public class LoopQueue<E> implements Queue<E> {

    private E[] data;
    private int front, tail;
    private int size;

    public LoopQueue(int capacity){
        data = (E[])new Object[capacity + 1];
        front = 0;
        tail = 0;
        size = 0;
    }

    public LoopQueue(){
        this(10);
    }

    public int getCapacity(){
        return data.length - 1;
    }

    @Override
    public boolean isEmpty(){
        return front == tail;
    }

    @Override
    public int getSize(){
        return size;
    }

    @Override
    public void enqueue(E e){

        if((tail + 1) % data.length == front)
            resize(getCapacity() * 2);

        data[tail] = e;
        tail = (tail + 1) % data.length;
        size ++;
    }

    @Override
    public E dequeue(){

        if(isEmpty())
            throw new IllegalArgumentException("Cannot dequeue from an empty queue.");

        E ret = data[front];
        data[front] = null;
        front = (front + 1) % data.length;
        size --;
        if(size == getCapacity() / 4 && getCapacity() / 2 != 0)
            resize(getCapacity() / 2);
        return ret;
    }

    @Override
    public E getFront(){
        if(isEmpty())
            throw new IllegalArgumentException("Queue is empty.");
        return data[front];
    }

    private void resize(int newCapacity){

        E[] newData = (E[])new Object[newCapacity + 1];
        for(int i = 0 ; i < size ; i ++)
            newData[i] = data[(i + front) % data.length];

        data = newData;
        front = 0;
        tail = size;
    }

    @Override
    public String toString(){

        StringBuilder res = new StringBuilder();
        res.append(String.format("Queue: size = %d , capacity = %d\n", size, getCapacity()));
        res.append("front [");
        for(int i = front ; i != tail ; i = (i + 1) % data.length){
            res.append(data[i]);
            if((i + 1) % data.length != tail)
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }
}

簡單復(fù)雜度分析

  • void enquque(E):O(1)(均攤)
  • E dequeue()O(1)(均攤)
  • E front():O(1)
  • int getSize():O(1)
  • boolean isEmpty():O(1)

簡單數(shù)組隊(duì)列和循環(huán)隊(duì)列的簡單比較

我們來簡單對比一下兩個隊(duì)列的性能吧,這里直接上代碼:

// 測試使用q運(yùn)行opCount個enqueueu和dequeue操作所需要的時間暇赤,單位:秒
private static double testQueue(Queue<Integer> q, int opCount){

    long startTime = System.nanoTime();

    Random random = new Random();
    for(int i = 0 ; i < opCount ; i ++)
        q.enqueue(random.nextInt(Integer.MAX_VALUE));
    for(int i = 0 ; i < opCount ; i ++)
        q.dequeue();

    long endTime = System.nanoTime();

    return (endTime - startTime) / 1000000000.0;
}

public static void main(String[] args) {

    int opCount = 100000;

    ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
    double time1 = testQueue(arrayQueue, opCount);
    System.out.println("ArrayQueue, time: " + time1 + " s");

    LoopQueue<Integer> loopQueue = new LoopQueue<>();
    double time2 = testQueue(loopQueue, opCount);
    System.out.println("LoopQueue, time: " + time2 + " s");
}

我這里的測試結(jié)果是這樣的心例,大家也就可見一斑啦:

其實(shí)ArrayQueue慢主要是因?yàn)槌鰲r每次都需要把整個結(jié)構(gòu)往前挪一下


LeetCode 相關(guān)題目整理

20.有效的括號

我的答案:(10ms)

public boolean isValid(String s) {

    // 正確性判斷
    if (null == s || s.length() == 1) {
        return false;
    }

    Stack<Character> stack = new Stack<>();
    // 遍歷輸入的字符
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        // 如果為左括號則push進(jìn)棧
        if (c == '(' || c == '[' || c == '{') {
            stack.push(c);
        } else {
            if (stack.isEmpty()) {
                return false;
            }

            char topChar = stack.pop();
            if (c == ')' && topChar != '(') {
                return false;
            }
            if (c == ']' && topChar != '[') {
                return false;
            }
            if (c == '}' && topChar != '{') {
                return false;
            }
        }
    }

    // 最后棧為空才能返回true
    return stack.isEmpty();
}

參考答案:(8ms)

public boolean isValid(String s) {

    // 正確性判斷
    if (0 == s.length()) {
        return true;
    }
    if (s.length() % 2 == 1) {
        return false;
    }

    Stack<Character> stack = new Stack();
    char[] cs = s.toCharArray();
    for (int i = 0; i < cs.length; i++) {
        if (cs[i] == '(' || cs[i] == '[' || cs[i] == '{') {
            stack.push(cs[i]);
        } else {
            if (stack.isEmpty()) {
                return false;
            }
            char c = stack.pop();
            if ((cs[i] == ')' && c == '(') || (cs[i] == '}' && c == '{') || (cs[i] == ']' && c == '[')) {
            } else {
                return false;
            }
        }
    }

    return stack.isEmpty();
}

155. 最小棧(劍指Offer面試題30)

參考答案(107ms)

class MinStack {

    // 數(shù)據(jù)棧,用于存放插入的數(shù)據(jù)
    private Stack<Integer> dataStack;
    // 最小數(shù)位置棧翎卓,存放數(shù)據(jù)棧中最小的數(shù)的位置
    private Stack<Integer> minStack;

    /**
     * initialize your data structure here.
     */
    public MinStack() {
        this.dataStack = new Stack<>();
        this.minStack = new Stack<>();
    }

    /**
     * 元素入棧
     *
     * @param x 入棧的元素
     */
    public void push(int x) {

        dataStack.push(x);

        // 如果最小棧是空的契邀,只要將元素入棧
        if (minStack.isEmpty()) {
            minStack.push(x);
        }
        // 如果最小棧中有數(shù)據(jù)
        else {
            minStack.push(Math.min(x, minStack.peek()));
        }
    }

    /**
     * 出棧方法
     */
    public void pop() {
        // 如果棧已經(jīng)為空,則返回(LeetCode不能拋異常...)
        if (dataStack.isEmpty()) {
            return;
        }

        // 如果有數(shù)據(jù)失暴,最小數(shù)位置棧和數(shù)據(jù)棧必定是有相同的元素個數(shù)坯门,
        // 兩個棧同時出棧
        minStack.pop();
        dataStack.pop();
    }

    /**
     * 返回棧頂元素
     *
     * @return 棧頂元素
     */
    public int top() {
        return dataStack.peek();
    }

    /**
     * 獲取棧中的最小元素
     *
     * @return 棧中的最小元素
     */
    public int getMin() {
        // 如果最小數(shù)公位置棧已經(jīng)為空(數(shù)據(jù)棧中已經(jīng)沒有數(shù)據(jù)了),則拋出異常
        if (minStack.isEmpty()) {
            return 0;
        }

        // 獲取數(shù)據(jù)占中的最小元素逗扒,并且返回結(jié)果
        return minStack.peek();
    }
}

改進(jìn)答案:

上面求解方法的主要問題在于古戴,每次push操作時,minStack也執(zhí)行了一次push操作(新元素或當(dāng)前的最小元素)矩肩,也就是說现恼,重復(fù)執(zhí)行了最小值的入棧操作,所以現(xiàn)在我們來修改算法降低空間復(fù)雜度黍檩。仍然需要設(shè)置一個minStack叉袍,但是只有當(dāng)從dataStack中出棧的元素等于minStack棧頂元素時,才對minStack執(zhí)行出棧的操作刽酱;也只有當(dāng)dataStack入棧的元素小于或等于當(dāng)前最小值時喳逛,才對minStack執(zhí)行入棧操作,下面就簡單寫一下了主要看一下出棧和入棧實(shí)現(xiàn)的邏輯就好了:

class MinStack {

    private Stack<Integer> dataStack;
    private Stack<Integer> minStack;

    public MinStack() {
        this.dataStack = new Stack<>();
        this.minStack = new Stack<>();
    }

    public void push(int x) {
        dataStack.push(x);
        if (minStack.isEmpty() || minStack.peek() >= (Integer) x) {
            minStack.push(x);
        }
    }
    
    public void pop() {
        if (dataStack.isEmpty()) {
            return;
        }
        Integer minTop = minStack.peek();
        Integer dataTop = dataStack.peek();
        if (minTop.intValue() == dataTop.intValue()) {
            minStack.pop();
        }
        dataStack.pop();
    }

    public int top() {
        return dataStack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

225. 用隊(duì)列實(shí)現(xiàn)棧

我的答案:(118ms)

class MyStack {

    private Queue<Integer> queue1;
    private Queue<Integer> queue2;

    /**
     * Initialize your data structure here.
     */
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    /**
     * Push element x onto stack.
     */
    public void push(int x) {
        if (queue1.isEmpty()) {
            queue2.offer(x);
        } else {
            queue1.offer(x);
        }
    }

    /**
     * Removes the element on top of the stack and returns that element.
     */
    public int pop() {
        int size;
        if (!queue1.isEmpty()) {
            size = queue1.size();
            for (int i = 0; i < size - 1; i++) {
                queue2.offer(queue1.poll());
            }
            return queue1.poll();
        } else {
            size = queue2.size();
            for (int i = 0; i < size - 1; i++) {
                queue1.offer(queue2.poll());
            }
            return queue2.poll();
        }
    }

    /**
     * Get the top element.
     */
    public int top() {
        int size;
        if (!queue1.isEmpty()) {
            size = queue1.size();
            for (int i = 0; i < size - 1; i++) {
                queue2.offer(queue1.poll());
            }
            int result = queue1.peek();
            queue2.offer(queue1.poll());
            return result;
        } else {
            size = queue2.size();
            for (int i = 0; i < size - 1; i++) {
                queue1.offer(queue2.poll());
            }
            int result = queue2.peek();
            queue1.offer(queue2.poll());
            return result;
        }
    }

    /**
     * Returns whether the stack is empty.
     */
    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
}

參考答案:(121ms)

class MyStack {
    Queue<Integer> q;

    /**
     * Initialize your data structure here.
     */
    public MyStack() {
        this.q = new LinkedList<Integer>();
    }

    /**
     * Push element x onto stack.
     */
    public void push(int x) {
        q.add(x);
    }

    /**
     * Removes the element on top of the stack and returns that element.
     */
    public int pop() {
        int size = q.size();
        for (int i = 0; i < size - 1; i++) {
            q.add(q.remove());
        }
        return q.remove();
    }

    /**
     * Get the top element.
     */
    public int top() {
        int size = q.size();
        for (int i = 0; i < size - 1; i++) {
            q.add(q.remove());
        }
        int ret = q.remove();
        q.add(ret);
        return ret;
    }

    /**
     * Returns whether the stack is empty.
     */
    public boolean empty() {
        return q.isEmpty();
    }
}

確實(shí)寫得簡潔啊棵里,這樣一來我就使用一個隊(duì)列和兩個隊(duì)列都掌握啦润文,開心~

232.用棧實(shí)現(xiàn)隊(duì)列(劍指Offer面試題9)

參考答案:(72ms)

class MyQueue {
    Stack<Integer> pushstack;
    Stack<Integer> popstack;

    /**
     * Initialize your data structure here.
     */
    public MyQueue() {
        this.pushstack = new Stack();
        this.popstack = new Stack();
    }

    /**
     * Push element x to the back of queue.
     */
    public void push(int x) {
        pushstack.push(x);
    }

    /**
     * Removes the element from in front of queue and returns that element.
     */
    public int pop() {
        if (popstack.isEmpty()) {
            while (!pushstack.isEmpty()) {
                popstack.push(pushstack.pop());
            }
        }
        return popstack.pop();
    }


    /**
     * Get the front element.
     */
    public int peek() {
        if (popstack.isEmpty()) {
            while (!pushstack.isEmpty()) {
                popstack.push(pushstack.pop());
            }
        }
        return popstack.peek();
    }

    /**
     * Returns whether the queue is empty.
     */
    public boolean empty() {
        return pushstack.isEmpty() && popstack.isEmpty();
    }
}

其他題目整理

劍指Offer面試題31:棧的壓入、彈出序列

題目:輸入兩個整數(shù)序列殿怜,第一個序列表示棧的壓入順序典蝌,請判斷第二個序列是否為該棧的彈出順序。假設(shè)壓入棧的所有數(shù)字均不相等头谜。例如骏掀,序列{1,2,3,4,5}是某棧的壓棧序列,序列{4,5,3,2,1}是該壓棧序列對應(yīng)的一個彈出序列柱告,但{4,3,5,1,2}就不可能是該壓棧序列的彈出序列砖织。

參考答案:(原文鏈接:https://blog.csdn.net/derrantcm/article/details/46691083

public class Test22 {
    /**
     * 輸入兩個整數(shù)序列,第一個序列表示棧的壓入順序末荐,請判斷二個序列是否為該棧的彈出順序。
     * 假設(shè)壓入棧的所有數(shù)字均不相等新锈。例如序列1 甲脏、2、3 、4块请、5 是某棧壓棧序列娜氏,
     * 序列4、5墩新、3贸弥、2、1是該壓棧序列對應(yīng)的一個彈出序列海渊,
     * 但4绵疲、3、5臣疑、1盔憨、2就不可能是該壓棋序列的彈出序列。
     * 【與書本的的方法不同】
     *
     * @param push 入棧序列
     * @param pop  出棧序列
     * @return true:出棧序列是入棧序列的一個彈出順序
     */
    public static boolean isPopOrder(int[] push, int[] pop) {
        // 輸入校驗(yàn)讯沈,參數(shù)不能為空郁岩,并且兩個數(shù)組中必須有數(shù)字,并且兩個數(shù)組中的數(shù)字個數(shù)相同
        // 否則返回false
        if (push == null || pop == null || pop.length == 0 || push.length == 0 || push.length != pop.length) {
            return false;
        }

        // 經(jīng)過上面的參數(shù)校驗(yàn)缺狠,兩個數(shù)組中一定有數(shù)據(jù)问慎,且數(shù)據(jù)數(shù)目相等
        // 用于存放入棧時的數(shù)據(jù)
        Stack<Integer> stack = new Stack<>();
        // 用于記錄入棧數(shù)組元素的處理位置
        int pushIndex = 0;
        // 用于記錄出棧數(shù)組元素的處理位置
        int popIndex = 0;
        // 如果還有出棧元素要處理
        while (popIndex < pop.length) {
            // 入棧元素還未全部入棧的條件下,如果棧為空挤茄,或者棧頂?shù)脑夭慌c當(dāng)前處理的相等如叼,則一直進(jìn)行棧操作,
            // 直到入棧元素全部入椡苑或者找到了一個與當(dāng)出棧元素相等的元素
            while (pushIndex < push.length && (stack.isEmpty() || stack.peek() != pop[popIndex])) {
                // 入棧數(shù)組中的元素入棧
                stack.push(push[pushIndex]);
                // 指向下一個要處理的入棧元素
                pushIndex++;
            }

            // 如果在上一步的入棧過程中找到了與出棧的元素相等的元素
            if (stack.peek() == pop[popIndex]) {
                // 將元素出棧
                stack.pop();
                // 處理下一個出棧元素
                popIndex++;
            }
            // 如果沒有找到與出棧元素相等的元素薇正,說明這個出棧順序是不合法的
            // 就返回false
            else {
                return false;
            }
        }

        // 下面的語句總是成立的
        // return stack.isEmpty();

        // 為什么可以直接返回true:對上面的外層while進(jìn)行分析可知道,對每一個入棧的元素囚衔,
        // 在stack棧中挖腰,通過一些入棧操作,總可以在棧頂上找到與入棧元素值相同的元素练湿,
        // 這就說明了這個出棧的順序是入棧順序的一個彈出隊(duì)列猴仑,這也可以解釋為什么stack.isEmpty()
        // 總是返回true,所有的入棧元素都可以進(jìn)棧肥哎,并且可以被匹配到辽俗,之后就彈出,最后棧中就無元素篡诽。
        return true;
    }

    /**
     * 輸入兩個整數(shù)序列崖飘,第一個序列表示棧的壓入順序,請判斷二個序列是否為該棧的彈出順序杈女。
     * 【按書本上的思路進(jìn)行求解朱浴,兩者相差不大】
     *
     * @param push 入棧序列
     * @param pop  出棧序列
     * @return true:出棧序列是入棧序列的一個彈出順序
     */
    public static boolean isPopOrder2(int[] push, int[] pop) {

        // 用于記錄判斷出棧順序是不是入棧順的一個出棧序列吊圾,默認(rèn)false
        boolean isPossible = false;

        // 當(dāng)入棧和出棧數(shù)組者都不為空,并且都有數(shù)據(jù)翰蠢,并且數(shù)據(jù)個數(shù)都相等
        if (push != null && pop != null && push.length > 0 && push.length == pop.length) {
            // 用于存放入棧時的數(shù)據(jù)
            Stack<Integer> stack = new Stack<>();
            // 記錄下一個要處理的入棧元素的位置
            int nextPush = 0;
            // 記錄下一個要處理的出棧元素的位置
            int nextPop = 0;
            // 如果出棧元素沒有處理完就繼續(xù)進(jìn)行處理
            while (nextPop < pop.length) {
                // 如果棧為空或者棧頂?shù)脑嘏c當(dāng)前處理的出棧元素不相同项乒,一直進(jìn)行操作
                while (stack.isEmpty() || stack.peek() != pop[nextPop]) {
                    // 如果入棧的元素已經(jīng)全部入棧了,就退出內(nèi)層循環(huán)
                    if (nextPush >= push.length) {
                        break;
                    }

                    // 執(zhí)行到此處說明還有入棧元素可以入棧
                    // 即將元素入棧
                    stack.push(push[nextPush]);
                    // 指向下一個要處理的入棧元素的位置
                    nextPush++;
                }

                // 執(zhí)行到此處有兩種情況:
                // 第一種:在棧頂上找到了一個與入棧元素相等的元素
                // 第二種:在棧頂上沒有找到一個與入棧元素相等的元素梁沧,而且輸入棧的元素已經(jīng)全部入棧了

                // 對于第二種情況就說彈出棧的順序是不符合要求的檀何,退出外層循環(huán)
                if (stack.peek() != pop[nextPop]) {
                    break;
                }

                // 對應(yīng)到第一種情況:需要要棧的棧頂元素彈出
                stack.pop();
                // 指向下一個要處理的出棧元素的位置
                nextPop++;
            }

            // 執(zhí)行到此處有兩種情況
            // 第一種:外層while循環(huán)的在第一種情況下退出,
            // 第二種:所有的出棧元素都被正確匹配

            // 對于出現(xiàn)的第一種情況其stack.isEmpty()必不為空廷支,原因?yàn)榉治鋈缦拢?            // 所有的入棧元素一定會入棧频鉴,但是只有匹配的情況下才會出棧,
            // 匹配的次數(shù)最多與入棧元素個數(shù)元素相同(兩個數(shù)組的長度相等)酥泞,如果有不匹配的元素砚殿,
            // 必然會使出棧的次數(shù)比入棧的次數(shù)少,這樣棧中至少會有一個元素
            // 對于第二種情況其stack.isEmpty()一定為空
            // 所以書本上的nextPop == pop.length(pNextPop-pPop==nLength)是多余的
            if (stack.isEmpty()) {
                isPossible = true;
            }
        }

        return isPossible;
    }

    public static void main(String[] args) {
        int[] push = {1, 2, 3, 4, 5};
        int[] pop1 = {4, 5, 3, 2, 1};
        int[] pop2 = {3, 5, 4, 2, 1};
        int[] pop3 = {4, 3, 5, 1, 2};
        int[] pop4 = {3, 5, 4, 1, 2};

        System.out.println("true: " + isPopOrder(push, pop1));
        System.out.println("true: " + isPopOrder(push, pop2));
        System.out.println("false: " + isPopOrder(push, pop3));
        System.out.println("false: " + isPopOrder(push, pop4));

        int[] push5 = {1};
        int[] pop5 = {2};
        System.out.println("false: " + isPopOrder(push5, pop5));

        int[] push6 = {1};
        int[] pop6 = {1};
        System.out.println("true: " + isPopOrder(push6, pop6));

        System.out.println("false: " + isPopOrder(null, null));

        // 測試方法2
        System.out.println();
        System.out.println("true: " + isPopOrder2(push, pop1));
        System.out.println("true: " + isPopOrder2(push, pop2));
        System.out.println("false: " + isPopOrder2(push, pop3));
        System.out.println("false: " + isPopOrder2(push, pop4));
        System.out.println("false: " + isPopOrder2(push5, pop5));
        System.out.println("true: " + isPopOrder2(push6, pop6));
        System.out.println("false: " + isPopOrder2(null, null));
    }
}

簡單總結(jié)

棧和隊(duì)列的應(yīng)用遠(yuǎn)不止上面學(xué)習(xí)到的那些芝囤,實(shí)現(xiàn)方式也有很多種似炎,現(xiàn)在也只是暫時學(xué)到這里,通過刷LeetCode也加深了我對于這兩種數(shù)據(jù)結(jié)構(gòu)的認(rèn)識悯姊,不過自己還需要去熟悉了解一下計(jì)算機(jī)系統(tǒng)關(guān)于棧的應(yīng)用這方面的知識羡藐,因?yàn)闂_@種結(jié)構(gòu)本身就很適合用來保存CPU現(xiàn)場之類的工作,還是抓緊時間吧悯许,過兩天還考試仆嗦,這兩天就先復(fù)習(xí)啦...

歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處先壕!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關(guān)注公眾微信號:wmyskxz_javaweb
分享自己的Java Web學(xué)習(xí)之路以及各種Java學(xué)習(xí)資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘩扼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子垃僚,更是在濱河造成了極大的恐慌集绰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谆棺,死亡現(xiàn)場離奇詭異栽燕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)改淑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門碍岔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朵夏,你說我怎么就攤上這事蔼啦。” “怎么了仰猖?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵捏肢,是天一觀的道長掠河。 經(jīng)常有香客問我,道長猛计,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任爆捞,我火速辦了婚禮奉瘤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘煮甥。我一直安慰自己盗温,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布成肘。 她就那樣靜靜地躺著卖局,像睡著了一般。 火紅的嫁衣襯著肌膚如雪双霍。 梳的紋絲不亂的頭發(fā)上砚偶,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機(jī)與錄音洒闸,去河邊找鬼染坯。 笑死,一個胖子當(dāng)著我的面吹牛丘逸,可吹牛的內(nèi)容都是我干的单鹿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼深纲,長吁一口氣:“原來是場噩夢啊……” “哼仲锄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起湃鹊,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤儒喊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后涛舍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澄惊,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年富雅,在試婚紗的時候發(fā)現(xiàn)自己被綠了掸驱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡没佑,死狀恐怖毕贼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛤奢,我是刑警寧澤鬼癣,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布陶贼,位于F島的核電站,受9級特大地震影響待秃,放射性物質(zhì)發(fā)生泄漏拜秧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一章郁、第九天 我趴在偏房一處隱蔽的房頂上張望枉氮。 院中可真熱鬧,春花似錦暖庄、人聲如沸聊替。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惹悄。三九已至,卻和暖如春肩钠,著一層夾襖步出監(jiān)牢的瞬間泣港,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工蔬将, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爷速,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓霞怀,卻偏偏與公主長得像惫东,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子毙石,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容