数据结构之——数组和链表

版权声明:本文为博主原创文章,未经博主允许不得转载 ! https://blog.csdn.net/seniusen/article/details/83002859

1. 数组

1.1 数组为什么从零编号?

数组名代表数组的首地址,数组的下标其实代表数组中某个元素相对首地址的偏移量,数组的第一个元素是零偏移,因此从 0 开始。

上面其实也只是一个解释, C 语言设计者用零开始编号,后来的各种语言也便纷纷效仿,因此就形成了这个习惯。

1.2 数组的特点?

  • 数组是一种线性结构,在内存上按顺序存放,支持随机访问,但需要足够的连续内存空间。在数组中增加或者删除元素时需要大量的移动操作,效率较低。

  • 警惕数组越界,对数组进行越界访问时就相当于在其他未知内存上进行了操作,这些区域可能 原本存放着重要的系统信息或者其他数据,错误操作会对程序带来无法估计的结果。

  • 容器支持动态扩容,且一般封装了许多操作的方法,但如果事先知道数组大小,或者表示多维数据,还是可以考虑使用数组这种数据结构。


2. 链表

2.1 链表的特点?

  • 不需要连续的内存,通过指针的关联可以任意存放在零散的空间上。在链表中插入或者删除数据比较方便,只需要更改相邻两个节点即可,但查找某一特定元素效率较低,只能从头指针开始一个一个地按照顺序访问

2.2 常见链表分类?

  • 单链表
    单链表

单链表只支持一个方向的访问,第一个节点称为头结点,最后一个节点称为尾结点。

  • 循环链表
    循环链表

循环链表是一种特殊的单向链表,其尾结点指向头结点,从链尾访问链头比较方便。

  • 双向链表
    双向链表

双向链表的每一个结点同时指向其前面的结点和其后面的结点,因此可以双向访问,但两个指针要占用更多的内存空间。

在向链表中插入或者删除某个结点时,我们需要知道该结点的前向结点,这时候,双向链表就显示出其优势来了。

  • 双向循环链表
    双向循环链表

3. 数组 VS 链表

  • 插入、删除元素时时间复杂度,数组为 O ( n ) O(n) ,链表为 O ( 1 ) O(1)

  • 随机访问时间复杂度,数组为 O ( 1 ) O(1) ,链表为 O ( n ) O(n)

  • 数组简单易用,还可以借助 CPU 的缓存机制,预读数据,访问效率更高,但一经申请就大小固定。

  • 链表本身没有大小的限制,支持动态扩容,但每个结点都需要额外的空间存放指针,频繁的删除和插入操作还会造成内存碎片。


4. 链表的一些注意事项

  • 指针和引用都可以看作是变量的地址,通过地址可以访问到变量。

  • 警惕指针丢失和内存泄露。链表中丢失了某一结点的指针,其后所有的数据都会丢失,另外,删除节点时一定记得手动释放内存。

  • 利用哨兵简化实现难度。没有哨兵,在头结点插入新的结点和在中间位置插入新的节点操作可能会不一样。但引入哨兵后就只有一种情况,可以简化实现难度。

  • 留意边界条件,重点考虑链表为空、链表只有一个结点和链表头尾结点的处理情况。

参考资料-极客时间专栏《数据结构与算法之美》

获取更多精彩,请关注「seniusen」!
seniusen

猜你喜欢

转载自blog.csdn.net/seniusen/article/details/83002859