棧的實現原理

目錄介紹

  • 01.棧由簡單數據實現php

    • 1.1 簡單數組代碼實現
    • 1.2 可能出現問題
    • 1.3 性能和侷限性
  • 02.棧由動態數組實現node

    • 2.1 基於簡單數組存在問題
    • 2.2 第一種解決辦法
    • 2.3 第二種解決辦法
    • 2.4 動態數組實現棧代碼
    • 2.5 性能和侷限性
  • 03.棧由鏈表實現git

    • 3.1 使用鏈表的優點
    • 3.2 鏈表實現棧代碼
    • 3.3 性能和侷限性
  • 04.Android棧Stack源碼分析github

    • 4.1 源碼展現
    • 4.2 爲什麼選用數組實現棧
  • 05.建立增強版自定義棧面試

    • 5.1 擴容和泛型

好消息

  • 博客筆記大彙總【16年3月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong2...
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!

棧系列文章

01.棧由簡單數組實現

1.1 簡單數組代碼實現

  • 首先看一下實現的代碼

    • 用數組實現棧,最主要的是要在類的內部定義一個數組,而且這個數組要具備必定的大小,要在定義棧的時候定義好。
    public class ArrayStack{
        private static final String TAG = "ArrayStack";
        private Object[] contents;
        private int top = -1;
        private int bottom = -1;
        private int SIZE = 10;//有一個初始值大小
    
        public ArrayStack(){
            contents = new Object[SIZE];
            top = -1;
        }
    
        public int push(Object obj) throws Exception {
            if (top > SIZE) throw new Exception("小楊逗比,棧已經滿了!");
            top++;
            contents[top] = obj;
            return top;
        }
    
        public Object pop() throws Exception{
            if (top == bottom) throw new Exception("小楊逗比,棧已經空了!");
            Object obj = contents[top];
            contents[top] = null;
            top--;
            return obj;
        }
    
        public boolean isEmpty(){
            return top == bottom;
        }
    
        public int getSize(){
            return top + 1;
        }
    
        public void display() throws Exception{
            if (getSize() == 0) throw new Exception("空棧!");
            for (int i=getSize()-1;i>=0;i--){
                System.out.print(contents[i].toString() + "->");
            }
            System.out.println("");
        }
    } 
    
    
    public void test{
        ArrayStack as = new ArrayStack();
        //as.display();
        as.push("小楊逗比");
        as.push("瀟湘劍雨");
        as.push("yc");
        as.push("逗比");
        as.push("aaa");
        as.push("ertte");
        as.push("hello");
        as.display();
        as.pop();
        System.out.println(as.getSize());
        as.pop();
        as.display();
    }

1.2 可能出現問題

  • 可能會出現的問題

    • 當數組棧存滿了元素的時候,若是執行插入數據,將會拋出棧滿異常。
    • 當數組棧沒有元素的時候,若是執行出棧數據,將會拋出棧空異常。

1.3 性能和侷限性

  • 性能和侷限性分析

    • 棧的最大空間必須提早聲明,並且關鍵是大小還不能改變,這就蛋疼了。因此會出現執行入棧或者出棧操做時會出現異常。那麼解決異常就是每次入棧判斷棧是否存儲滿,每次出棧判斷棧是否爲空。
    • 假設棧中有m個元素,基於簡單數組實現的棧。棧的出棧,入棧,判空,獲取大小等時間複雜度都是O(1)。

02.棧由動態數組實現

2.1 基於簡單數組存在問題

  • 基於簡單數組的棧實現方法中,採用一個下標變量top,它始終指向棧中最新插入元素的位置。
  • 當插入元素時,會增長top值,而且會在數組該下標的位置存儲新的元素。
  • 當刪除元素時,先獲取下標變量top位置的元素,而後減少變量top的值。
  • 當top下標變量爲-1時,表示棧是空的。可是存在問題是:在固定大小的數組中,如何處理全部空間都已經保存棧元素這種狀況???

2.2 第一種解決辦法

  • 可能首先會想到,每次將數組大小增長1或者減少1,將會怎麼樣?

    • 插入元素,棧的空間大小增長1
    • 刪除元素,棧的空間大小減去1
  • 這樣作存在極大問題

    • 頻繁增長數組大小的方法開銷很大。爲何這樣說呢?
    • 當n=3時,執行push插入元素操做,當插入第四條元素時,會新建一個大小爲4的數組,而後複製原數組中全部的元素到新的數組中,而後在新的數組中的末尾添加插入元素。以此類推,每次插入數據,都會從新建立一個新的數組對象,而後拷貝舊的數組數據到新的數組中來,而後在末尾添加新元素,這樣作實在很差。

2.3 第二種解決辦法

  • 在第一種解決辦法中改造。好比咱們常常聽到ArrayList集合動態擴容,先指定數組的長度,若是數組空間已滿,則新建一個比原數據大一倍[或者n倍]的新數組,再而後複製元素。
  • 採用這種方式,插入元素操做,開銷相對來講要小不少。

2.4 動態數組實現棧代碼

  • 基於動態數據實現棧的代碼以下所示

    class DynArrayStack{
        private int top;
        private int capacity;
        private int[] array;
     
        private void doubleStack(){
            int[] newArray=new int[capacity*2];
            System.arraycopy(array,0,newArray,0,capacity);
            capacity=capacity*2;
            array=newArray;
        }
     
        public DynArrayStack(){
            top=-1;
            capacity=1;
            array=new int[capacity];
        }
     
        public boolean isEmpty(){
            return (top==-1);
        }
     
        public boolean isStackFull(){
            return (top==capacity-1);
        }
     
        public void push(int date){
            if(isStackFull()){
                doubleStack();
            }
            array[++top]=date;
        }
     
        public int pop(){
            if(isEmpty()){
                System.out.println("Stack Empty");
                return 0;
            }else {
                return array[top--];
            }
        }
     
        public void deleteStack(){
            top=-1;
        }
    }
     
    public class Main {
     
        public static void main(String[] args) {
            // write your code here
            DynArrayStack dynArrayStack=new DynArrayStack();
            dynArrayStack.push(1);
            dynArrayStack.push(2);
            dynArrayStack.push(3);
            System.out.println(dynArrayStack.pop());
            System.out.println(dynArrayStack.pop());
            System.out.println(dynArrayStack.pop());
        }
    }

2.5 性能和侷限性

  • 性能

    • 假設有n個元素的棧,基於動態數組的棧實現中,關於棧插入數據,取出數據的時間複雜度都是O(1)。
    • 可能致使的性能問題:倍增太多可能致使內存溢出。
  • 存在侷限性

    • 是用數組實現棧,在定義數組類型的時候,也就規定了存儲在棧中的數據類型,那麼同一個棧能不能存儲不一樣類型的數據呢?(聲明爲Object)?
    • 棧須要初始化容量,並且數組實現的棧元素都是連續存儲的,那麼能不能不初始化容量呢?(改成由鏈表實現)?

03.棧由鏈表實現

3.1 使用鏈表的優點

  • 棧規模的增長和減少都很簡潔,並且每一個操做都是常數時間開銷,每一個操做都要使用額外的空間和時間開銷來處理指針。

3.2 鏈表實現棧代碼

  • 入棧的順序是:1 2 3 4 5,那麼保證出棧的順序是5 4 3 2 1,以此類推讓head節點指向棧頂節點保證讓其倒序輸出。

    public class MyStack<T> {
        private T data;
        private MyStack<T> next;
     
        public MyStack(T data, MyStack<T> next) {
            this.data = data;
            this.next = next;
        }
     
        public T getData() {
            return data;
        }
     
        public void setData(T data) {
            this.data = data;
        }
     
        public MyStack<T> getNext() {
            return next;
        }
     
        public void setNext(MyStack<T> next) {
            this.next = next;
        }
    }
    
    public class LinkStack<N> {
     
        private MyStack<N> head;
        private MyStack<N> tail;
        private Integer size=0;
     
        public MyStack<N> getHead() {
            return head;
        }
     
        public void setHead(MyStack<N> head) {
            this.head = head;
        }
     
        public MyStack<N> getTail() {
            return tail;
        }
     
        public void setTail(MyStack<N> tail) {
            this.tail = tail;
        }
     
        public Integer getSize() {
            return size;
        }
     
        public void setSize(Integer size) {
            this.size = size;
        }
     
        public void addStack(N data){
            MyStack<N> node = new MyStack<>(data,null);
            if(headIsNull()){
                head = node;
                tail = node;
                size++;
            }else{
                //新加入的node是:(data,null) 讓這個新的node的next指向初始的head節點 head變爲(data,head))
                node.setNext(head);
                head = node;
                size++;
            }
        }
     
        public N outStack(){
            if(size>0){
                N outData = head.getData();
                head = head.getNext();
                return outData;
            }else{
                throw new RuntimeException("棧裏無元素!");
            }
        }
     
        public boolean headIsNull(){
            if(head == null){
                return true;
            }
            return false;
        }
    }
  • 測試一下

    public void test() {
        LinkStack<Integer> linkStack = new LinkStack<>();
        linkStack.addStack(1);
        linkStack.addStack(2);
        linkStack.addStack(3);
        linkStack.addStack(4);
        linkStack.addStack(5);
    
        for(int i=0;i<linkStack.getSize();i++){
            System.out.println(linkStack.outStack());
        }
    }

3.3 性能和侷限性

  • 假設棧中有n個元素,基於鏈表的棧實現中,關於棧插入元素和取出元素的時間複雜度是O(1)
  • 數據入棧和出棧的時間複雜度都爲O(1),也就是說棧操做所耗的時間不依賴棧中數據項的個數,所以操做時間很短。並且須要注意的是棧不須要比較和移動操做。

04.棧Stack源碼分析

  • 在Android中,對於activity使用棧stack進行管理的,下面看看棧源代碼。

    • 能夠看到棧stack是實現vector,其實底層也是用數組來實現的。
    public class Stack<E> extends Vector<E> {
        /**
         * 建立一個空的棧對象
         */
        public Stack() {
        }
    
        /**
         * 將對象推送到此堆棧的頂部。
         */
        public E push(E item) {
            addElement(item);
    
            return item;
        }
    
        /**
         * 移除此堆棧頂部的對象,並將該對象做爲此函數的值返回。
         */
        public synchronized E pop() {
            E       obj;
            int     len = size();
            obj = peek();
            removeElementAt(len - 1);
    
            return obj;
        }
    
        /**
         * 查看此堆棧頂部的對象,而不將其從堆棧中移除。
         */
        public synchronized E peek() {
            int     len = size();
    
            if (len == 0)
                throw new EmptyStackException();
            return elementAt(len - 1);
        }
    
        /**
         * 判斷是不是空棧
         */
        public boolean empty() {
            return size() == 0;
        }
    
        /**
         * 返回對象位於此堆棧上的基於1的位置。
         */
        public synchronized int search(Object o) {
            int i = lastIndexOf(o);
    
            if (i >= 0) {
                return size() - i;
            }
            return -1;
        }
    
        private static final long serialVersionUID = 1224463164541339165L;
    }

05.建立增強版自定義棧

  • 一個能自動擴容,第二個能存儲各類不一樣類型的數據,解決辦法以下:

    public class ArrayStack {
        //存儲元素的數組,聲明爲Object類型能存儲任意類型的數據
        private Object[] elementData;
        //指向棧頂的指針
        private int top;
        //棧的總容量
        private int size;
         
         
        //默認構造一個容量爲10的棧
        public ArrayStack(){
            this.elementData = new Object[10];
            this.top = -1;
            this.size = 10;
        }
         
        public ArrayStack(int initialCapacity){
            if(initialCapacity < 0){
                throw new IllegalArgumentException("棧初始容量不能小於0: "+initialCapacity);
            }
            this.elementData = new Object[initialCapacity];
            this.top = -1;
            this.size = initialCapacity;
        }
         
         
        //壓入元素
        public Object push(Object item){
            //是否須要擴容
            isGrow(top+1);
            elementData[++top] = item;
            return item;
        }
         
        //彈出棧頂元素
        public Object pop(){
            Object obj = peek();
            remove(top);
            return obj;
        }
         
        //獲取棧頂元素
        public Object peek(){
            if(top == -1){
                throw new EmptyStackException();
            }
            return elementData[top];
        }
        //判斷棧是否爲空
        public boolean isEmpty(){
            return (top == -1);
        }
         
        //刪除棧頂元素
        public void remove(int top){
            //棧頂元素置爲null
            elementData[top] = null;
            this.top--;
        }
         
        /**
         * 是否須要擴容,若是須要,則擴大一倍並返回true,不須要則返回false
         * @param minCapacity
*/
    public boolean isGrow(int minCapacity){
        int oldCapacity = size;
        //若是當前元素壓入棧以後總容量大於前面定義的容量,則須要擴容
        if(minCapacity >= oldCapacity){
            //定義擴大以後棧的總容量
            int newCapacity = 0;
            //棧容量擴大兩倍(左移一位)看是否超過int類型所表示的最大範圍
            if((oldCapacity<<1) - Integer.MAX_VALUE >0){
                newCapacity = Integer.MAX_VALUE;
            }else{
                newCapacity = (oldCapacity<<1);//左移一位,至關於*2
            }
            this.size = newCapacity;
            int[] newArray = new int[size];
            elementData = Arrays.copyOf(elementData, size);
            return true;
        }else{
            return false;
        }
    }
}
```

其餘介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索