美文网首页
数组和链表

数组和链表

作者: 麦兜的夏天 | 来源:发表于2019-05-26 01:30 被阅读0次

数组

数组是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

  • 线性表
    表上的数据最多之后前后两个方向。常见的线性表结构有:数组、链表、队列、栈等

  • 连续的内存空间和相同类型的数据
    因为这两个限制,使得数组的“随机访问”相对高效。
    带来的弊端是增删操作变得非常低效。原因是,为了保证连续性,需要做大量的数据搬移工作。

(1)数组适合查找操作,但是要说查找的时间复杂度是 O(1),是不严谨的。比如排好序的数组,使用二分查找的时间复杂度为 O(logn)。严谨的说法是,根据下标随机访问的时间复杂度为 O(1)。

根据下标随机访问高效的原因:
因为数组的内存空间是连续的,并且每个元素占用的内存大小相同(数据类型相同),所以通过公式很容易计算出指定元素的地址。

a[i]_address = base_address + i * data_type_size

a[i]_address : i 号元素的内存地址
base_address : 内存块的首地址
data_type_size : 数组中每个元素的大小。例:int 类型,就是4字节

(2)增删操作效率低的改进办法:

  • 增:假如需要向第 k 个位置插入数据。
    如果数据没有排序或者其他规律,直接将第 k 个位置上的数据拿出来,放到末尾,然后将新的数据放在这个位置上。

  • 删:假如要删除第 k 个位置的数据
    将多次删除集中在一起执行。
    具体操作是,每次对需要删除的数据做标记,当数组空间不足时再真正触发删除操作,从而大大减少数据搬移。这和 JVM 标记清除垃圾回收算法的核心思想一致。

(3)容器能否完全代替数组?

  • 容器的优点:
    1.将数组操作的细节封装了起来
    2.支持动态扩展
    因为数据扩展涉及内存申请和数据搬移,比较耗时,所以如果实现确定需要存储的数据大小,最好在创建的时候事先指定数据大小。

  • 容器的缺点:无法存储基本类型,装箱和拆箱有一定的性能消耗,如果特别关注性能,可以使用数组。


链表

链表与数组的区别是,它不像数组那样使用连续的内存空间,而是通过“指针”将一组零散的内存块串联起来。

常见的链表有:单链表、双向链表和循环链表等。

一、

(1)单链表 单链表.png

链表中,每个蓝色快被叫做节点,Next 叫做指针。

(2)循环链表

它是一种特殊的单链表。只是将结尾节点的指针指向头节点。 image.png 当要处理的数据具有环形特征的时候,就采用循环链表。比如著名的约瑟夫问题。
(3)双向链表 image.png 双向链表额外的两个空间用来存储后继节点和前驱节点的地址。因此它不像单链表那样只支持单项遍历,它可以支持双向遍历。

二、链表与数组相比的优缺点:

  • 优点:数组在插入、删除操作的时候,由于需要做大量的数据搬移,所以时间复杂度为O(n)。而链表的存储空间本身就不是连续的,在做同样操作的时候不需要搬移数据,理论上讲时间复杂度是 O(1) (单链表和双向链表的复杂度不一样)。

  • 缺点是:链表想要随机访问第 k 个元素,需要根据指针一个个节点进行遍历,时间复杂度为 O(n)。

(1)单链表和双向链表在增删操作的时候时间复杂度不一样在什么地方?
以删除操作为例。
实际开发中,从链表中删除一个数据无外乎有两种情况:

1.删除节点中“值等于某个给定值”的节点
2.删除某一指针指向的节点

  • 第一种情况:无论是单链表还是双向链表,为了找到给定值的节点,都需要从头到尾依次遍历对比,直到找到这个给定值的节点,再将其删除。尽管单纯的删除操作时间复杂度是 O(1),但是查找的过程确实主要的消耗时间,对应的时间复杂度是 O(n)。根据加法法则,得出总的操作时间复杂度为 O(n)。

  • 第二种情况:我们已经知道要删除的节点了,但是要删除某个节点 p 需要知道其前驱节点(删完之后还要讲旁边的节点连接气起来),而单链表不支持直接获取前驱节点,所以需要遍历整个链表,时间复杂度为 O(n)。
    而双向链表已经保存了前驱节点的指针,不需要遍历整个表,只需要在 O(1) 的时间复杂度内就可以搞定。

这也是实际开发中,双向链表尽管很费内存,但却应用更加广泛的原因。java 中的 LinkedHashMap 就用到了双链表数据结构。这就是用空间换时间的设计思想。

(2)链表 VS 数组性能大比拼


image.png

不过数组与链表的对比,并不能局限于时间复杂度。

数据使用的是连续的内存空间,可以借助 CPU 的缓存机制,预读数组中的数据,所以访问效率更高。而链表在内存中并不是连续存储的,所以对 CPU 的缓存不友好,无法预读。

数组的缺点是大小固定,一经生命就要占用整块连续内存空间。如果生命的数组过大,可能会导致内存不足“out of memory”,而如果申请的数组过小,内可能出现不够用的情况,这时再去申请内存空间,又牵扯到数据搬移的问题,比较费时。链表则天然的支持动态扩容。

此外,如果代码对内存的要求比较苛刻,那么数组更加合适。因为链表的每个节点都需要额外的空间去存储下个节点的指针,所以内存消耗会翻倍。而且,对链表进行频繁的插入、删除操作,可能会导致频繁的内存申请和释放,Java 语言中可能会导致频繁的 GC。

相关文章

  • iOS知识复习笔记(19)---数据结构和算法1

    数组和链表的区别 数组静态分配内存,链表动态分配内存 数组内存中连续,链表不连续 数组元素在栈区,链表在堆区 数组...

  • 数据结构和算法

    线性结构 数组、 单链表和双链表 数组和链表区别: 数组:数组元素在内存上连续存放,可以通过下标查找元素;插入、删...

  • Redis-第十章节-链表

    目录 数组和链表 链表 对比 总结 1、数组和链表 数组: 数组会在内存中开辟一块连续的空间存储数据,这种存储...

  • 数据结构——链表

    目录 1、属性 2、链表和数组的区别 2.1、数组概述 2.2、数组和链表优缺点 2.3、链表和数组的比较 3、单...

  • 链表

    链表和数组一样也支持查找,插入,删除操作。最常见的三种链表是:单链表,双链表和循环链表。 链表和数组的区别:数组需...

  • C语言指针部分说明

    二级指针 函数指针 数组和链表 访问数组 访问链表 Makefile

  • 算法小专栏:选择排序

    本篇将重点介绍选择排序,在讲解选择排序之前,我们先复习一下数组和链表等知识。 一、数组 or 链表? 数组和链表作...

  • HashMap 相关面试题及其解答

    Q:HashMap 的数据结构?A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过 ...

  • 这21个刁钻的HashMap面试题,我把阿里面试官吊打了

    1:HashMap 的数据结构? A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过...

  • 面试:HashMap 夺命二十一问!

    1:HashMap 的数据结构? A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过...

网友评论

      本文标题:数组和链表

      本文链接:https://www.haomeiwen.com/subject/jwjttctx.html