鏈表和順序表的一些區別

順序表與鏈表是很是基本的數據結構,它們能夠被統稱爲線性表。
線性表(Linear List)是由 n(n≥0)個數據元素(結點)a[0],a[1],a[2]…,a[n-1] 組成的有限序列。
順序表和鏈表,是線性表的不一樣存儲結構。它們各自有不一樣的特色和適用範圍。針對它們各自的缺點,也有不少改進的措施。node

1、順序表

順序表通常表現爲數組,使用一組地址連續的存儲單元依次存儲數據元素,如圖 1 所示。它具備以下特色:sql

  • 長度固定,必須在分配內存以前肯定數組的長度。
  • 存儲空間連續,即容許元素的隨機訪問。
  • 存儲密度大,內存中存儲的所有是數據元素。
  • 要訪問特定元素,可使用索引訪問,時間複雜度爲 。
  • 要想在順序表中插入或刪除一個元素,都涉及到以後全部元素的移動,所以時間複雜度爲 。

    圖 1 順序表

    順序表最主要的問題就是要求長度是固定的,可使用倍增-複製的辦法來支持動態擴容,將順序表變成「可變長度」的。
    具體作法是初始狀況使用一個初始容量(能夠指定)的數組,當元素個數超過數組的長度時,就從新申請一個長度爲原先二倍的數組,並將舊的數據複製過去,這樣就能夠有新的空間來存放元素了。這樣,列表看起來就是可變長度的。
    一個簡單的實現以下所示,初始的容量爲 4。
#include <string.h>
 
struct sqlist {
    int *items, size, capacity;
    sqlist():size(0), capacity(4) {
        // initial capacity = 4
        items = new int[capacity];
    }
    void doubleCapacity() {
        capacity *= 2;
        int* newItems = new int[capacity];
        memcpy(newItems, items, sizeof(int)*size);
        delete[] items;
        items = newItems;
    }
    void add(int value) {
        if (size >= capacity) {
            doubleCapacity();
        }
        items[size++] = value;
    }
};

這個辦法不可避免的會浪費一些內存,由於數組的容量老是倍增的。並且每次擴容的時候,都須要將舊的數據所有複製一份,確定會影響效率。不過實際上,這樣作仍是直接使用鏈表的效率要高,具體緣由會在下一節進行分析。數組

2、鏈表

鏈表,相似它的名字,表中的每一個節點都保存有指向下一個節點的指針,全部節點串成一條鏈。根據指針的不一樣,還有單鏈表、雙鏈表和循環鏈表的區分,如圖 2 所示。
數據結構


圖 2 鏈表

單鏈表是隻包含指向下一個節點的指針,只能單向遍歷。
雙鏈表即包含指向下一個節點的指針,也包含指向前一個節點的指針,所以能夠雙向遍歷。
循環單鏈表則是將尾節點與首節點連接起來,造成了一個環狀結構,在某些狀況下會很是有用。
還有循環雙鏈表,與循環單鏈表相似,這裏就再也不贅述。
因爲鏈表是使用指針將節點連起來,所以無需使用連續的空間,它具備如下特色:

  • 長度不固定,能夠任意增刪。
  • 存儲空間不連續,數據元素之間使用指針相連,每一個數據元素只能訪問周圍的一個元素(根據單鏈表仍是雙鏈表有所不一樣)。
  • 存儲密度小,由於每一個數據元素,都須要額外存儲一個指向下一元素的指針(雙鏈表則須要兩個指針)。
  • 要訪問特定元素,只能從鏈表頭開始,遍歷到該元素,時間複雜度爲 。
  • 在特定的數據元素以後插入或刪除元素,不涉及到其餘元素的移動,所以時間複雜度爲 。雙鏈表還容許在特定的數據元素以前插入或刪除元素。
    在上一節說到,利用倍增-複製的辦法,一樣可讓順序表長度可變,並且效率比鏈表還要好,下面就簡單的實現一個單鏈表來驗證這一點,至於元素插入的順序就不要在乎了。
#include <stdio.h>
#include <time.h>
  
struct node {
    int value;
    node *next;
};
struct llist {
    node *head;
    void add(int value) {
        node *newNode = new node();
        newNode->value = value;
        newNode->next = head;
        head = newNode;
    }
};
 
int main() {
    int size = 100000;
    sqlist list1;
    llist list2;
    long start = clock();
    for (int i = 0;i < size;i++) {
        list1.add(i);
    }
    long end = clock();
    printf("sequence list: %d\n", end - start);
    start = clock();
    for (int i = 0;i < size;i++) {
        list2.add(i);
    }
    end = clock();
    printf("linked list: %d\n", end - start);
    return 0;
}

在個人電腦上,鏈表的耗時大約是順序表的 4~8 倍。會這樣,是由於數組只須要不多的幾回大塊內存分配,而鏈表則須要不少次小塊內存分配,內存分配操做相對是比較慢的,於是大大拖慢了鏈表的速度。這也是爲何會出現內存池。指針

所以,鏈表並不像理論分析的那樣美好,在實際應用中要受不少條件制約,通常狀況下仍是安心用順序表的好。code

相關文章
相關標籤/搜索