重溫四大基礎數據結構:數組、鏈表、隊列和棧

file

前言

本文收錄於專輯: http://dwz.win/HjK,點擊解鎖更多數據結構與算法的知識。

你好,我是彤哥,一個天天爬二十六層樓還不忘讀源碼的硬核男人。java

數組、鏈表、隊列、棧,是數據結構中最基礎的四大結構,數組和鏈表更是基礎中的基礎,後續全部複雜的數據結構都是在它們的基礎上演變而來的。node

本節,咱們就來重溫這四大結構。算法

數組

關於數組,你們都比較熟悉了。數組

它是一種線性數據結構,使用一組連續的內存空間存儲一組具備相同類型的數據。數據結構

file

這個概念中有三個關鍵詞:線性、連續、相同類型。架構

線性,表示沒有分叉,任意元素的先後元素最多隻有一個,一樣是線性結構的還有鏈表、隊列等。性能

連續,它在內存空間中的存儲是連續的,不間斷的,先後兩個元素緊挨着,不存在間隙。學習

相同類型,數組中存儲的元素的類型必定是相同的,固然,在Java中,你可使用Object表明全部類型,本質上,它們依然是相同類型。this

正是有了上面三個特性,才使得數組具備了隨機訪問的特性,那麼,什麼是隨機訪問呢?spa

簡單點說,你能夠經過下標快速定位到數組中的元素,且時間複雜度是O(1),它是怎麼作到的呢?

咱們知道,計算機中只有0和1,一切的一切均可以看做是0和1的各類組合,內存也是同樣。

當咱們建立一個數組,好比int[] array = new int[]{2, 5, 8, 7};時,它其實返回的是這個數組在內存中的位置(地址),咱們知道,一個int類型佔用4個字節,也就是32位的0或1,當咱們訪問數組下標爲0的元素時,直接返回數組地址處取32位轉成int便可,一樣地,當咱們訪問數組下標爲1的元素時,返回數組地址加上(32*1)的地址處取32位轉成int,依此類推。

file

這也是大部分語言中數組下標從0開始的緣由,試想若是下標從1開始,那麼,計算內存地址的時候就變成了address + 32 * (i - 1),這顯然會形成必定的性能損耗。

鏈表

鏈表,它也是一種線程數據結構,與數組不一樣的是,它在內存空間中不必定是順序存儲的,爲了保證鏈表中元素的連續性,通常使用一個指針來找到下一個元素。

file

上圖是典型的單鏈表結構,在單鏈表中,只有一個指向下一個元素的指針。

若是要用Java類來表示單鏈表中的元素節點的話,大概看起來像這樣子:

class Node {
    int value;
    Node next;
}

因此,鏈表不具備隨機訪問的特性,在鏈表中根據索引來查找元素只能從頭開始(單鏈表),它的時間複雜度是O(n)。

上面咱們說的是單鏈表,若是在單鏈表的基礎上再增長一個前驅指針(指向前一個元素的指針),就變成了雙向鏈表。

file

Java中的LinkedList就是典型的雙向鏈表結構,雙向鏈表既能夠看成隊列使用,又能夠看成棧來使用,很是方便。

若是在雙向鏈表的基礎上再增長HashMap的功能,就變成了LinkedHashMap了,咳咳,扯遠了。

但願學習LinkedList和LinkedHashMap源碼解析的同窗,能夠關注個人公號主「彤哥讀源碼」。

這裏提到了隊列,那麼,什麼是隊列呢?

隊列

所謂隊列,其實跟現實中的排隊是同樣的,其中的元素從一端進入,從另外一端出去,英文叫作:First In,First Out,簡寫FIFO。

file

從這張圖,也能夠看出來,實現隊列最簡單的方式就是使用鏈表,把上圖中的箭頭倒過來便可。

file

入隊時,將元素加入到鏈表尾端,出隊時,將第一個元素刪除並將頭節點指向下一個節點便可。

讓咱們來看看使用鏈表實現隊列的簡單代碼實現:

public class LinkedQueue {
    Node head;
    Node tail;

    void offer(Integer value) {
        if (value == null) {
            throw new NullPointerException();
        }
        Node node = new Node(value);
        if (head == null) {
            head = tail = node;
        } else {
            tail.next = node;
            tail = node;
        }
    }

    Integer poll() {
        Node first = head;
        if (first != null) {
            head = first.next;
            first.next = null;
            return first.value;
        } else {
            return null;
        }
    }

    static class Node {
        int value;
        Node next;

        public Node(int value) {
            this.value = value;
        }
    }
}

是否是很簡單呢?

那麼,數組能不能實現隊列呢?

答案是確定的,使用數組實現隊列有不少種方式,其中一種是使用兩個指針:入指針、出指針,它們分別指向下一個入隊列和下一個出隊列的位置。

入隊時,在入指針處放入元素,同時入指針後移。

出隊時,取出出指針處的元素返回,同時出指針後移。

當指針到達數組末尾時,返回數組開始的位置。

這樣就造成了一個能夠循環使用的數組,俗稱環形數組。

file

此時,咱們考慮一個問題,隊列空和隊列滿時,兩個指針都是指向同一個位置,彷佛不太好處理。

其實,很簡單,引入一個size變量標識隊列中有多少個元素便可。

因此,這玩意兒要怎麼實現呢?Show me the code!

public class ArrayQueue {
    int[] array;
    int offerIndex;
    int pollIndex;
    int size;

    public ArrayQueue(int capacity) {
        this.array = new int[capacity];
        this.offerIndex = this.pollIndex = 0;
        this.size = 0;
    }

    boolean offer(Integer value) {
        if (value == null) {
            throw new NullPointerException();
        }

        if (size == array.length) {
            return false;
        }

        array[offerIndex] = value;
        offerIndex = (offerIndex + 1) % array.length;

        size++;

        return true;
    }

    Integer poll() {
        if (size == 0) {
            return null;
        }

        int value = array[pollIndex];
        pollIndex = (pollIndex + 1) % array.length;

        size--;

        return value;
    }
}

OK,以上就是使用數組實現的隊列,能夠看到,與鏈表實現的隊列相比,它須要指定容量,這叫作有界隊列,若是須要使用數組實現無界隊列,則須要加入擴容的機制,有興趣的同窗能夠本身實現看看。

下面,咱們再來看另外一種基礎的數據結構——棧。

棧,它是與隊列表現徹底相反的數據結構,它的元素是先進的後出來,就像咱們往一個杯子裏面放東西同樣,先放進去的放在最下面,只有把上面的東西拿出來後才能拿出下面壓着的東西,這種行爲用英文叫作:First In,Last Out,簡稱FILO。

file

棧,具備不少用處,計算機中不少處理都是經過棧這種數據結構來進行的,好比算術運算,準備兩個棧,一個棧存儲數字,一個棧存儲符號,從頭開始依次把字符壓入到這兩個棧中,當遇到符號優先級比棧頂元素低時,則取出棧頂符號,並從數字棧中取出兩個數字進行運算,運算的結果再壓回數字棧中,繼續以此運行,當全部字符都放入棧以後,依次從數字棧中取出兩個元素,並從符號棧中取出一個元素,進行計算,結果壓回數字棧,繼續以此運行,直到符號棧爲空,或者數字棧只剩下一個元素爲止,彈出這個數字即爲最後的結果。

3 + 2 * 4 -1爲例:

file

好了,關於棧,咱們就簡單介紹到這裏,後面,咱們還會大量遇到這個數據結構。

後記

本節,咱們一塊兒重溫了數組、鏈表、隊列、棧這四種最基礎的數據結構。

提及數組,咱們看到,內存自己就是一張大數組,它裏面的元素就是0和1,那麼,咱們能不能直接操做這些0和1呢?

答案是確定的。

下一節,咱們將介紹位運算,以及位圖這種數據結構,彼時,咱們將詳細介紹如何使用位圖來實現12306的搶票邏輯,關注我,及時獲取最新推文。

關注公號主「彤哥讀源碼」,解鎖更多源碼、基礎、架構知識。

file

相關文章
相關標籤/搜索