我是一個棧

我是一個棧,先來跟你介紹一下個人家族,個人家族是一個很古老的家族,家族成員不少,外界稱呼咱們這個家族爲數據結構。咱們是計算機世界中存儲和組織數據的。數據結構家族在計算機世界中是相當重要的,由於咱們家族功能的強大,現代編程語言及其API中都有咱們家族成員的身影。java

個人家族是一個龐大的家族。家族中也有幾大分支,好比說樹、圖、堆、散列表等。各個分支都有不一樣的能力,因此不少人選擇適當的數據結構是一項很重要的工做。咱們家族和算法家族是世交,基本上全部重要場合兩家都會一塊兒出現。算法

我叫棧,個人爸爸叫數組,個人媽媽叫鏈表,個人雙胞胎弟弟叫隊列。咱們這個家庭是整個數據結構家族中比較重要的家庭。編程

和你說過了,咱們數據結構家族是計算機世界中儲存和組織數據的。個人家族之因此這麼強大,就是由於咱們要應付各類需求,提供不一樣的數據存儲方式。個人四個家庭成員分別能夠解決不一樣的數據存取需求。數組

數組

提及個人爸爸——數組,他是數據結構家族的族長,人們都說他是數據結構你們族的根基。不少編程語言都內置數組。爸爸是個很富有的人,他有不少土地。每當有人找他來存儲數據的時候,他都會預先劃分出一塊連續的土地(連續的內存),而後把別人交給他的數據按順序存儲在這些連續的土地中,當別人來取這些數據的時候,須要提供給數組要取哪塊土地中的數據(數組的位置索引),而後數組就能夠直接去那塊土地中把數據取出來,交給須要讀取這個數據的人。並非全部數據數組都能幫忙存儲的,父親只會幫別人存儲相同類型的數據。數據結構

數組

個人家族的全部人都支持幾個基本的操做:插入、刪除、讀取。app

由於數組爸爸在存儲數據的時候,是按照順序保存的,他保存數據的土地是一塊一塊相連的。那麼,他的特色就是尋址讀取數據比較容易,可是插入和刪除比較困難。這個其實比較好理解。讀取容易的緣由是隻要你告訴數組你要從哪塊土地讀取數據,他就會直接去那塊土地中把數據取出來給你。插入和刪除比較麻煩,主要是由於這些存儲數據的空間都是連着的,好比編號爲0-1-2-3-4這五塊土地中都保存了數據,可是如今你要往1中插入一塊數據,那就意味着從1開始,後面的全部土地中的數據都要日後移一個位置。這是很大的工做量啊。編程語言

數組1

鏈表

人們都說,男女搭配,幹活不累。因爲數組爸爸有尋址容易,插入和刪除困難的問題,他在找老婆的時候特地找了一個和本身互補的姑娘——鏈表。鏈表媽媽的特色正好是尋址困難,插入和刪除容易。測試

在幫助別人存儲數據這件事上,鏈表的方式和數組有很大不一樣。數組是提早劃分了一大片連續的土地,用來存儲數據。可是鏈表不是這麼作的,由於鏈表媽媽家裏並無數組爸爸家裏那麼富有。他的數據存儲並非連續的,能這麼作的緣由是媽媽存儲數據的土地中有兩塊區域,一塊用來保存數據,一塊用來記錄下一個數據保存在哪塊土地中(指針)。這樣,別人找他存儲數據的時候,她就要根據指示先找到下一塊空餘的土地,而後把數據保存在裏面。鏈表這樣的數據存儲方式能夠把一些碎片空間利用起來。ui

鏈表

經過上面的這種保存數據的方式,鏈表在插入和刪除數據的時候比較容易,好比編號爲0-1-2-3-4這五塊土地中都保存了數據,可是如今你要往1中插入一塊數據,那麼只須要依次更改0號土地和1號土地中記錄下一塊土地地址的值就好了。對於其餘的數據存儲沒有任何影響。可是,若是你想從數據中取出一塊數據那可就麻煩了,由於這意味着鏈表媽媽要幫你從第一塊土地開始挨個幫你找。一直找到你要的數據爲止。this

鏈表1

我和個人胞弟

棧,也就是我,一個英俊瀟灑的數據結構。我和隊列是一堆孿生兄弟。咱們兩個均可以用數組和鏈表來實現。雖然是雙胞胎,可是咱們兩個都是有性格的,咱們要求別人存儲數據和取出數據的順序要按照咱們的規矩來。

個人原則是:先進後出(棧)

stack

弟弟的原則是:先進先出(隊列)

queue

我給你舉個例子你就明白了,我和弟弟每一個人都有一個管子,用來幫大家保存數據,固然這個管子多是用數組爸爸是實現的也多是鏈表媽媽實現的。咱們握住管子的兩頭,個人這個管子只能經過管子左面的口子放入東西,也只能從左面的口子取出東西。右面的口子是不開的。而弟弟隊列呢,他的管子的左面的口子放東西,管子的右面的口子取東西。

爸爸媽媽是如何生出我和弟弟的

上面說過了,棧和隊列都是能夠經過數組或者鏈表實現的。固然了,父母創造孩子,天經地義嘛。那麼,先來看看我是如何實現的。做爲一種數據結構,我接口有isEmpty()、size()、push()、pop()、peek()以及迭代。

先來看看如何經過數組實現棧:

public class Stack<Item> implements Iterable<Item> {
    private Item[] a;         // 數組表示棧,棧頂在最大的下標。
    private int n;            // 棧內元素的個數

    /**
     * 初始化一個空棧
     */
    public Stack() {
        a = (Item[]) new Object[2];
        n = 0;
    }

    /**
     * 判斷棧內是否有元素
     */
    public boolean isEmpty() {
        return n == 0;
    }

    /**
     * 返回棧內元素個數
     */
    public int size() {
        return n;
    }

    // 改變棧的大小
    private void resize(int capacity) {
        assert capacity >= n;
        // 注意不能直接建立泛型數組
        Item[] temp = (Item[]) new Object[capacity];
        for (int i = 0; i < n; i++) {
            temp[i] = a[i];
        }
        a = temp;
       // 也能夠選擇下面這種方式改變數組大小
       // a = java.util.Arrays.copyOf(a, capacity);
    }

    /**
     * 壓入元素
     */
    public void push(Item item) {
        //先判斷n的大小,若是棧滿則改變棧的大小
        if (n == a.length) resize(2*a.length);    
        a[n++] = item;                
    }

    /**
     * 彈出並返回元素
     */
    public Item pop() {
        if (isEmpty()) throw new NoSuchElementException("Stack underflow");
        Item item = a[n-1];
        a[n-1] = null;   //防止對象遊離
        n--;
        // 若是有必要則調整棧的大小
        if (n > 0 && n == a.length/4) resize(a.length/2);
        return item;
    }


    /**
     * 返回但不彈出棧頂元素
     */
    public Item peek() {
        if (isEmpty()) throw new NoSuchElementException("Stack underflow");
        return a[n-1];
    }

    /**
     * 返回一個能夠進行先進後出迭代的迭代器
     */
    public Iterator<Item> iterator() {
        return new ReverseArrayIterator();
    }

    // 用內部類實現迭代器接口,實現從棧頂往棧底的先進後出迭代,沒有實現remove()方法。 
    private class ReverseArrayIterator implements Iterator<Item> {
        private int i;
        public ReverseArrayIterator() {
            i = n-1;
        }
        public boolean hasNext() {
            return i >= 0;
        }
        public void remove() {
            throw new UnsupportedOperationException();
        }
        public Item next() {
            if (!hasNext()) throw new NoSuchElementException();
            return a[i--];
        }
    }


    /**
     * 測試
     */
    public static void main(String[] args) {
        Stack<String> stack = new Stack<String>();
        while (!StdIn.isEmpty()) {
            String item = StdIn.readString();
            if (!item.equals("-")) stack.push(item);
            else if (!stack.isEmpty()) StdOut.print(stack.pop() + " ");
        }
        StdOut.println("(" + stack.size() + " left on stack)");
    }
}
複製代碼

再來看看如何使用鏈表實現棧:

public class Stack<Item> implements Iterable<Item> {
    private Node<Item> first;     //棧頂節點
    private int N;                // 棧內元素數量

    // 輔助類Node,用於造成鏈表
    private static class Node<Item> {
        private Item item;
        private Node<Item> next;
    }

    /**
     * 初始化棧
     */
    public Stack() {
        first = null;
        N = 0;
    }

    /**
     * 判斷棧是否爲空
     */
    public boolean isEmpty() {
        return first == null;
        //return N == 0;
    }

    /**
     * 返回棧內元素數量
     */
    public int size() {
        return N;
    }

    /**
     * 壓入元素
     */
    public void push(Item item) {
        Node<Item> oldfirst = first;
        first = new Node<Item>();
        first.item = item;
        first.next = oldfirst;
        N++;
    }

    /**
     * 彈出元素
     */
    public Item pop() {
        if (isEmpty()) throw new NoSuchElementException("Stack underflow");
        Item item = first.item;        // 需彈出的元素
        first = first.next;            // 刪除頭節點
        N--;
        return item;       
    }


    /**
     * 返回但不彈出元素
     */
    public Item peek() {
        if (isEmpty()) throw new NoSuchElementException("Stack underflow");
        return first.item;
    }

    /**
     * 從棧頂到棧底打印元素
     */
    public String toString() {
        StringBuilder s = new StringBuilder();
        for (Item item : this)
            s.append(item + " ");
        return s.toString();
    }


    /**
     * 實現Iterable接口
     */
    public Iterator<Item> iterator() {
        return new ListIterator<Item>(first);
    }

    // 實現Iterator接口用於迭代,沒有實現remove方法
    private class ListIterator<Item> implements Iterator<Item> {
        private Node<Item> current;

        //初始化時,current指向棧頂
        public ListIterator(Node<Item> first) {
            current = first;
        }

        public boolean hasNext() {
            return current != null;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        public Item next() {
            if (!hasNext()) throw new NoSuchElementException();
            Item item = current.item;
            current = current.next; 
            return item;
        }
    }


    /**
     * 測試
     */
    public static void main(String[] args) {
        Stack<String> s = new Stack<String>();
        while (!StdIn.isEmpty()) {
            String item = StdIn.readString();
            if (!item.equals("-")) s.push(item);
            else if (!s.isEmpty()) StdOut.print(s.pop() + " ");
        }
        StdOut.println("(" + s.size() + " left on stack)");
    }
}
複製代碼

一樣做爲數據結構的弟弟,也有些接口須要實現:isEmpty()、size()、enqueue()、dequeue()、peek()以及迭代。隊列和棧不一樣的是,入列和出列是在兩個地方,因此須要維護兩個變量來表示隊頭和隊尾。

使用數組實現隊列:

public class Queue<Item> implements Iterable<Item> {
    private Item[] q;       
    private int N;          // 隊列中元素的數量
    private int first;      // 隊頭元素的下標
    private int last;       // 隊尾元素的後一個位置的下標,也就是元素入列時能夠放置的位置


    /**
     * 初始化隊列,此時頭尾下標重合
     */
    public Queue() {
        q = (Item[]) new Object[2];
        N = 0;
        first = 0;
        last = 0;
    }

    /**
     * 依舊用N判斷隊列是否爲空
     */
    public boolean isEmpty() {
        return N == 0;
    }

    /**
     * 隊列中元素數量
     */
    public int size() {
        return N;
    }

    // 調整數組大小
    private void resize(int max) {
        assert max >= N;
        Item[] temp = (Item[]) new Object[max];
        //注意這裏:把N個元素放入總大小爲max的隊列(max>=N)
        //由於循環使用數組,從first開始的第i個元素可能保存在了first
        //前面(即last在first前面)。
        for (int i = 0; i < N; i++) {
            temp[i] = q[(first + i) % q.length];
        }
        q = temp;
        //把小隊列按順序複製到大隊列後重置隊頭和隊尾
        first = 0;
        last  = N;
    }

    /**
     * 元素入列
     */
    public void enqueue(Item item) {
        if (N == q.length) resize(2*q.length);  
        q[last++] = item;   // 元素入列
        if (last == q.length) last = 0;  // 若是last超出數組下標,把last置零,循環利用數組
        N++;
    }

    /**
     * 元素出列
     */
    public Item dequeue() {
        if (isEmpty()) throw new NoSuchElementException("Queue underflow");
        Item item = q[first];
        q[first] = null;       // 防止對象遊離
        N--;
        first++;
        if (first == q.length) first = 0; // 循環利用數組,下一個隊頭在下標爲0的地方
        if (N > 0 && N == q.length/4) resize(q.length/2); 
        return item;
    }

    /**
     * 返回隊頭元素但不出列
     */
    public Item peek() {
        if (isEmpty()) throw new NoSuchElementException("Queue underflow");
        return q[first];
    }
    /**
     * 實現Iterable接口
     */
    public Iterator<Item> iterator() {
        return new ArrayIterator();
    }

    //實現迭代器
    private class ArrayIterator implements Iterator<Item> {
        //維護一個i用於迭代
        private int i = 0;
        public boolean hasNext()  { return i < N; }
        public void remove()      { throw new UnsupportedOperationException();  }

        //直接利用first進行遍歷,注意可能存在數組的循環利用
        public Item next() {
            if (!hasNext()) throw new NoSuchElementException();
            Item item = q[(i + first) % q.length];
            i++;
            return item;
        }
    }

   /**
     * 測試
     */
    public static void main(String[] args) {
        Queue <String> q = new Queue <String>();
        while (!StdIn.isEmpty()) {
            String item = StdIn.readString();
            if (!item.equals("-")) q.enqueue(item);
            else if (!q.isEmpty()) StdOut.print(q.dequeue() + " ");
        }
        StdOut.println("(" + q.size() + " left on queue)");
    }

}
複製代碼

使用鏈表實現隊列:

public class Queue<Item> implements Iterable<Item> {
    private Node<Item> first;    // 隊頭節點
    private Node<Item> last;     // 隊尾節點(注意和上面的last區分,last並非隊尾元素的下標)
    private int N;               // 隊列元素的數量

    // 輔助類Node
    private static class Node<Item> {
        private Item item;
        private Node<Item> next;
    }

    /**
     * 初始化隊列
     */
    public Queue() {
        first = null;
        last  = null;
        N = 0;
    }

    public boolean isEmpty() {
        return first == null;
    }

    public int size() {
        return N;     
    }

    /**
     * 返回但不刪除頭元素
     */
    public Item peek() {
        if (isEmpty()) throw new NoSuchElementException("Queue underflow");
        return first.item;
    }

    /**
     * 元素入列
     */
    public void enqueue(Item item) {
        //記錄尾節點
        Node<Item> oldlast = last;
        //建立新的尾節點
        last = new Node<Item>();
        last.item = item;
        last.next = null;
        //若是隊列是空的,將first置爲last,由於這時候隊列中只有一個元素
        if (isEmpty()) first = last;
        //不然執行正常的在尾節點插入新節點的操做
        else           oldlast.next = last;
        N++;
    }

    /**
     *元素出列 
     */
    public Item dequeue() {
        if (isEmpty()) throw new NoSuchElementException("Queue underflow");
        //隊頭元素出列
        Item item = first.item;
        first = first.next;
        N--;
        //若是這時候隊列爲空,表示原來只有一個元素,這時候也將last置爲null
        if (isEmpty()) last = null;  
        return item;
    }

    public String toString() {
        StringBuilder s = new StringBuilder();
        for (Item item : this)
            s.append(item + " ");
        return s.toString();
    } 
    public Iterator<Item> iterator()  {
        return new ListIterator<Item>(first);  
    }

    // 實現迭代
    private class ListIterator<Item> implements Iterator<Item> {

        private Node<Item> current;
        //要實現迭代,咱們只須要維護一個節點,並在開始的時候將它置爲first
        public ListIterator(Node<Item> first) {
            current = first;
        }

        public boolean hasNext()  { return current != null;}
        public void remove()      { throw new UnsupportedOperationException();  }

        public Item next() {
            if (!hasNext()) throw new NoSuchElementException();
            Item item = current.item;
            current = current.next; 
            return item;
        }
    }


    /**
     * 測試
     */
    public static void main(String[] args) {
        Queue<String> q = new Queue<String>();
        while (!StdIn.isEmpty()) {
            String item = StdIn.readString();
            if (!item.equals("-")) q.enqueue(item);
            else if (!q.isEmpty()) StdOut.print(q.dequeue() + " ");
        }
        StdOut.println("(" + q.size() + " left on queue)");
    }
}
複製代碼

我是一個棧,個人雙胞胎弟弟叫隊列。個人爸爸是數組,個人媽媽是鏈表。 大家可使用數組和鏈表來實現棧和隊列。

好了,今天關於我和個人家族的故事就先給你介紹到這裏了。偷偷告訴你一個祕密,其實我和弟弟之間是能夠互相轉換的。也就是說可使用棧來實現隊列,一樣也可使用隊列來實現棧。關於這個小祕密,我下次再給你介紹哦。

參考資料

「數組、堆棧」與「鏈表、隊列」的區別

相關文章
相關標籤/搜索