博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
链表和顺序表的一些区别
阅读量:5816 次
发布时间:2019-06-18

本文共 2549 字,大约阅读时间需要 8 分钟。

顺序表与链表是非常基本的数据结构,它们可以被统称为线性表。

线性表(Linear List)是由 n(n≥0)个数据元素(结点)a[0],a[1],a[2]…,a[n-1] 组成的有限序列。
顺序表和链表,是线性表的不同存储结构。它们各自有不同的特点和适用范围。针对它们各自的缺点,也有很多改进的措施。

一、顺序表

顺序表一般表现为数组,使用一组地址连续的存储单元依次存储数据元素,如图 1 所示。它具有如下特点:

  • 长度固定,必须在分配内存之前确定数组的长度。
  • 存储空间连续,即允许元素的随机访问。
  • 存储密度大,内存中存储的全部是数据元素。
  • 要访问特定元素,可以使用索引访问,时间复杂度为 。
  • 要想在顺序表中插入或删除一个元素,都涉及到之后所有元素的移动,因此时间复杂度为 。
    1113280-20181025204906572-1603264835.png
    图 1 顺序表
    顺序表最主要的问题就是要求长度是固定的,可以使用倍增-复制的办法来支持动态扩容,将顺序表变成“可变长度”的。
    具体做法是初始情况使用一个初始容量(可以指定)的数组,当元素个数超过数组的长度时,就重新申请一个长度为原先二倍的数组,并将旧的数据复制过去,这样就可以有新的空间来存放元素了。这样,列表看起来就是可变长度的。
    一个简单的实现如下所示,初始的容量为 4。
#include 
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 所示。

1113280-20181025204920035-1301420023.png
图 2 链表
单链表是只包含指向下一个节点的指针,只能单向遍历。
双链表即包含指向下一个节点的指针,也包含指向前一个节点的指针,因此可以双向遍历。
循环单链表则是将尾节点与首节点链接起来,形成了一个环状结构,在某些情况下会非常有用。
还有循环双链表,与循环单链表类似,这里就不再赘述。
由于链表是使用指针将节点连起来,因此无需使用连续的空间,它具有以下特点:
  • 长度不固定,可以任意增删。
  • 存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素(根据单链表还是双链表有所不同)。
  • 存储密度小,因为每个数据元素,都需要额外存储一个指向下一元素的指针(双链表则需要两个指针)。
  • 要访问特定元素,只能从链表头开始,遍历到该元素,时间复杂度为 。
  • 在特定的数据元素之后插入或删除元素,不涉及到其他元素的移动,因此时间复杂度为 。双链表还允许在特定的数据元素之前插入或删除元素。
    在上一节说到,利用倍增-复制的办法,同样可以让顺序表长度可变,而且效率比链表还要好,下面就简单的实现一个单链表来验证这一点,至于元素插入的顺序就不要在意了。
#include 
#include
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 倍。会这样,是因为数组只需要很少的几次大块内存分配,而链表则需要很多次小块内存分配,内存分配操作相对是比较慢的,因而大大拖慢了链表的速度。这也是为什么会出现内存池。

因此,链表并不像理论分析的那样美好,在实际应用中要受很多条件制约,一般情况下还是安心用顺序表的好。

转载于:https://www.cnblogs.com/treasure716/p/9852774.html

你可能感兴趣的文章
Hadoop生态圈-Kafka常用命令总结
查看>>
如何基于Redis Replication设计并实现Redis-replicator?
查看>>
Linux 环境下 PHP 扩展的编译与安装 以 mysqli 为例
查看>>
开发uniapp必备
查看>>
TPS和QPS的区别和理解
查看>>
【BZOJ】3669: [Noi2014]魔法森林(lct+特殊的技巧)
查看>>
关于JavaScript中计算精度丢失的问题
查看>>
HashMap以List作为Key值存在的问题
查看>>
Stealth视频教程学习笔记(第一章)
查看>>
Android 设置控件可见与不可见
查看>>
生产服务器环境最小化安装后 Centos 6.5优化配置备忘
查看>>
使用NPOI从Excel中提取图片及图片位置信息
查看>>
股价上涨,资金流出以及内外盘的关系
查看>>
-/bin/sh: /usr/bin/xxx: not found”
查看>>
实现可以滑动的GrildView,类似美团网首页的GrildView功能菜单
查看>>
Wireshark数据抓包教程之认识捕获分析数据包
查看>>
WCF中的由于目标计算机积极拒绝,无法连接
查看>>
HBase的Shell命令
查看>>
poj 3390 Print Words in Lines 动态规划
查看>>
ubuntu安装mysql--参考的网址
查看>>