資料結構之——棧

DobbyKim發表於2019-04-19

資料結構學習—— 棧(stack)

  • 什麼是棧
  • 無處不在的棧——棧的應用
  • 使用陣列實現ArrayStack及時間複雜度分析
  • Leetcode20題:有效的括號

什麼是棧(stack)

棧(stack)是一種運算受限的線性資料結構,運算受限指的是棧這種資料結構僅允許在一端新增元素,刪除元素,這一端被稱作棧頂,相對應的另一端則稱為棧底。如圖所示:

stack.jpg

當前棧,如果想要新增元素“D”只能從棧頂部新增,從棧中取出元素則還是從棧頂開始取元素,所以棧是一種後進先出的資料結構即:LIFO(Last In First Out)。

無處不在的棧——棧的應用

一:Undo(撤銷操作)

當我們在文件編輯器中輸入文字,當發現輸入錯誤時,想要撤銷到前一步,這個操作就是Undo。撤銷的原理實際上就是棧這種資料結構來設計實現的。例如:李雷在某個文件編輯器上輸入文字“我愛韓梅梅”,結果,由於李雷滿腦子想的都是韓梅梅的音容笑貌,不小心將內容輸入成了“我愛含梅梅”。李雷想將內容恢復到“我愛” 這一步,所以他按了三次“Ctrl+z”,然後又依次將“韓”,“梅”,“梅”三個字輸入了進去。

Undo1

Undo2

Undo3

Undo(撤銷)看似很高階的操作,背後的原理就是棧。

二:C語言printf()函式

來看一個C語言的問題:

#include<stdio.h>
int main(void){
    int i=1;
    printf("%d%d%d",i,i++,i++);
    return 0;
}
複製程式碼

這個程式的執行結果是什麼?如果只是知道i++與++i的區別是不足以解決這道問題的。先公佈答案,這個程式的執行結果為:321,這與printf的底層原理有關,因為printf的底層實現就是棧。還是拿李雷韓梅梅來舉例說明。

printf("我愛韓梅梅");
複製程式碼

printf函式首先會將字串內容從右至左 push到棧中。

printf

然後,再將棧裡面的元素依次pop出來,這樣我們就能看到"我愛韓梅梅"這個字串被列印出來了。這道題也是一樣,首先將最右邊的%d即“i++” push到棧中,i==1,1入棧後,執行++操作,i的值變成了2。按照上述思路依次將所有元素推入棧中,棧的情況為:

printf2

將所有的元素出棧,出棧的順序就是我們看到的列印結果即:321。

三:程式呼叫系統棧

有如下程式:

A();
function A(){
1    ...
2    B();
3    ...
4    end
}
function B(){
1    ...
2    C();
3    ...
4    end
}
function C(){
1    ...
2    ...
3    ...
4    end
}
複製程式碼

程式從A方法開始呼叫,執行到 A方法的第二行,計算機發現需要執行B方法,這時就會將執行到哪一步這樣一個資訊壓入到系統棧中。例如,定義A2為A方法的第二行,計算機此時將A2壓入系統棧,表明執行到了A方法的第二行。

system

計算機在系統棧壓入這樣一個資訊後,開始執行B方法,執行到B方法的第二行,發現需要執行C方法,於是計算機將B2壓入系統棧中。

system

計算機開始執行C方法,C方法中沒有呼叫其他的函式,執行結束後,計算機發現系統棧中有殘留的任務,於是pop stack 發現需要回去執行完B方法,且執行到了B方法的第二行。B方法執行完畢後,計算機又去看了看系統棧,發現仍有殘留的任務需要執行,於是乎又 pop stack 發現原來A方法還沒有執行完畢,且執行到了第二行,所以計算機又將A方法執行完畢。這時系統棧為空,計算機終於鬆了一口氣,知道所有的任務已經執行完畢了~

使用陣列實現ArrayStack及時間複雜度分析

本文中ArrayStack的底層實現陣列為動態陣列:動態陣列,DobbyKim's Blog

public interface Stack<E> {
    void push(E e);
    E pop();
    E peek();
    int getSize();
    boolean isEmpty();
}
public class ArrayStack<E> implements Stack<E>{
    Array<E> array;
    public ArrayStack(int capacity){
        array = new Array<>(capacity);
    }
    public ArrayStack(){
        array = new Array<>();
    }
    public void push(E e){...}
    public E pop(){...}
    public int getSize(){...}
    public int getCapacity(){...}
    public boolean isEmpty(){...}
    public E peek(){...}
    public String toString(){...}
}
複製程式碼

點選檢視原始碼
ArrayStack的方法push 與 pop 的均攤時間複雜度為O(1),因為這裡面涉及到底層實現Array為動態陣列,resize()擴容操作為一個O(n)的演算法。getSize()方法,peek()方法,isEmpty()方法的時間複雜度均為O(1)。

Leetcode20題:有效的括號

給定一個只包括 '(',')','{','}','[',']' 的字串,判斷字串是否有效。
有效字串需滿足:
左括號必須用相同型別的右括號閉合。
左括號必須以正確的順序閉合。
注意空字串可被認為是有效字串。

  • 示例 1:
輸入: "()"
輸出: true
複製程式碼
  • 示例 2:
輸入: "()[]{}"
輸出: true
複製程式碼
  • 示例 3:
輸入: "(]"
輸出: false
複製程式碼
  • 示例 4:
輸入: "([)]"
輸出: false
複製程式碼
  • 示例 5:
輸入: "{[]}"
輸出: true
複製程式碼

問題解決思路:棧。只要是左側的括號為'(','[','{'就push到棧中,遇到與之匹配的右側括號則pop,最後棧如果為空則說明匹配成功。Java程式碼如下:

import java.util.Stack
class Solution {
	public boolean isValid(String s) {
        Stack<Character>stack = new Stack<>();
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)=='(' || s.charAt(i)=='[' || s.charAt(i)=='{'){
                stack.push(s.charAt(i));
            }else{
                if(stack.isEmpty())
                    return false;
                char c = stack.pop;
                if(s.charAt(i)==')' && c!='(')
                    return false;
                if(s.charAt(i)==']' && c!='[')
                    return false;
                if(s.charAt(i)=='}' && c!='{')
                    return false;
            }
        }
        return stack.isEmpty();
    }
}
複製程式碼

相關文章