We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
与数组类似,链表是一种线性结构,如图所示:
链表结构与数组结构有什么区别呢?
现在假设有这样一个场景:目前系统拥有的剩余内存空间充足,现在如果需要申请 100MB 的数组或者链表,系统中虽然剩余空间大于 100MB,但没有大于或等于 100MB 的连续内存块,那么数组将声明成功,链表因为不受内存连续影响,故而能够申请成功。
基于最基本的链表结构,链表也衍生出了几种不同的形式:
其结构如果所示:
上面的结构由一个个内存块连接组成,每个内存块称为结点,结点由两部分组成,一部分用来存储数据,一部分用来纯存储下一个节点的地址,也就是一个指针,这个指针我们称为后继指针。
除此之外,链表有两个特殊的结点,即第一个结点和最后一个结点,我们习惯性把它们称为头结点和尾结点:
如下图所示:
由上图可知,链表的插入与删除操作非常简单,只要经过一两步对后继指针的重新指向就可以完成,所以它们的时间复杂度为 O(1)。
另外,由于链表是由一系列零散的结点组成,无法进行类似数组通过下标访问对应结点的操作,查找每个结点都要从头结点开始一直遍历,直到找到相应的结点,所以时间复杂度为 O(n)。
其结构如图所示:
就跟“循环链表”这四个字所说的那样,该结构通过尾结点的后继指针指向头指针形成一个闭环结构,当要处理的数据具有环型结构特点时,就特别适合采用循环链表,如著名的约瑟夫问题。
双向链表是在单链表的基础之上在每个结点上增加了一个前驱指针(prev),以使得双向访问变得可能,如图所示:
双向链表在结构上比单链表多了前驱指针,也就表示这种结构将会占用更多的内存,那么在占用内存更高的情况下,其性能相比于单链表是怎样的呢?
首先从插入操作的角度看:
然后从删除操作的角度看:
最后,从查询的角度思考:
一般的情况下,两者都是遍历查询,时间复杂度为 O(n),但假如链表内的内容是有序的,双向链表会比单链表高效。因为我们每次查询之后可以记录上次查找的位置 p,然后根据查找值与 p 的大小关系决定往前还是往后查找,所以平均只需要查找一半的数据。
即双向链表与循环链表的结合,如图所示:
以上是两种数据结构一般情况下的时间复杂度,那么我们能够根据上面的时间复杂度来选择使用哪种数据结构吗?
不能,比如:
和数组相比,链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高。不过,在具体软件开发中,要对数组和链表的各种性能进行对比,综合来选择使用两者中的哪一个。
另外,在日常 coding 中,我们可以采用用空间换时间或者时间换空间的思想:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
其他平台文章地址
概况
与数组类似,链表是一种线性结构,如图所示:
链表结构与数组结构有什么区别呢?
现在假设有这样一个场景:目前系统拥有的剩余内存空间充足,现在如果需要申请 100MB 的数组或者链表,系统中虽然剩余空间大于 100MB,但没有大于或等于 100MB 的连续内存块,那么数组将声明成功,链表因为不受内存连续影响,故而能够申请成功。
基于最基本的链表结构,链表也衍生出了几种不同的形式:
单链表——最简单的链表形式
其结构如果所示:
上面的结构由一个个内存块连接组成,每个内存块称为结点,结点由两部分组成,一部分用来存储数据,一部分用来纯存储下一个节点的地址,也就是一个指针,这个指针我们称为后继指针。
除此之外,链表有两个特殊的结点,即第一个结点和最后一个结点,我们习惯性把它们称为头结点和尾结点:
链表的插入、删除、查找操作
如下图所示:
由上图可知,链表的插入与删除操作非常简单,只要经过一两步对后继指针的重新指向就可以完成,所以它们的时间复杂度为 O(1)。
另外,由于链表是由一系列零散的结点组成,无法进行类似数组通过下标访问对应结点的操作,查找每个结点都要从头结点开始一直遍历,直到找到相应的结点,所以时间复杂度为 O(n)。
循环链表
其结构如图所示:
就跟“循环链表”这四个字所说的那样,该结构通过尾结点的后继指针指向头指针形成一个闭环结构,当要处理的数据具有环型结构特点时,就特别适合采用循环链表,如著名的约瑟夫问题。
双向链表
双向链表是在单链表的基础之上在每个结点上增加了一个前驱指针(prev),以使得双向访问变得可能,如图所示:
双向链表在结构上比单链表多了前驱指针,也就表示这种结构将会占用更多的内存,那么在占用内存更高的情况下,其性能相比于单链表是怎样的呢?
首先从插入操作的角度看:
然后从删除操作的角度看:
最后,从查询的角度思考:
一般的情况下,两者都是遍历查询,时间复杂度为 O(n),但假如链表内的内容是有序的,双向链表会比单链表高效。因为我们每次查询之后可以记录上次查找的位置 p,然后根据查找值与 p 的大小关系决定往前还是往后查找,所以平均只需要查找一半的数据。
双向循环链表
即双向链表与循环链表的结合,如图所示:
链表与数组时间复杂度比较
以上是两种数据结构一般情况下的时间复杂度,那么我们能够根据上面的时间复杂度来选择使用哪种数据结构吗?
不能,比如:
总结
和数组相比,链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高。不过,在具体软件开发中,要对数组和链表的各种性能进行对比,综合来选择使用两者中的哪一个。
另外,在日常 coding 中,我们可以采用用空间换时间或者时间换空间的思想:
The text was updated successfully, but these errors were encountered: