链表和顺序表的一些区别

顺序表与链表是很是基本的数据结构,它们能够被统称为线性表。
线性表(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

相关文章
相关标签/搜索