好好学数据结构-链表

1,221 阅读3分钟

这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战

好好学数据结构-链表

链表

链表是一种用于表示一系列节点的数据结构。在单向链表中,每个节点指向链表中的下一个节点。而在双向链表中,每个节点同时具备指向前一个节点和后一个节点的指针。

下图描述了一个双向链表。

与数组不同的是,无法在常数时间复杂度内访问链表的一个特定索引。这意味着如果要访问链表中的第K个元素,需要迭代访问K个元素。

链表的好处在于你可以在常数时间复杂度内加入和删除元素。这对于某些特定的程序大有用处。

创建链表

下面的代码实现了一个非常基本的单向链表。

此实现中没有LinkedList数据结构,而是通过链表头节点Node的引用来访问链表。当你用这种方法实现链表时,需要小心。如果多个对象需要引用链表,而链表头节点变了,该怎么办?一些对象或许仍然指向旧的头节点。

可以选择实现一个LinkedList类来封装Node类。该类只包括一个成员变量:头节点Node。这样做可以在很大程度上解决上述问题。

切记:在面试中遇到链表题时,务必弄清楚它到底是单向链表还是双向链表。

删除单向链表中的节点

删除单向链表中的节点非常简单。给定一个节点n,先找到其前趋节点prev,并将prev.next设置为n.next。如果这是双向链表,还要更新n.next,将n.next.prev置为n.prev。当然,必须注意以下两点:(1) 检查空指针;(2) 必要时更新表头(head)或表尾(tail)指针。

此外,如果采用C、C++或其他要求开发人员自行管理内存的语言,还应考虑要不要释放删除节点的内存。

“快行指针”技巧

在处理链表问题时,“快行指针”(或称第二个指针)是一种很常见的技巧。“快行指针”指的是同时用两个指针来迭代访问链表,只不过其中一个比另一个超前一些。“快”指针往往先行几步,或与“慢”指针相差固定的步数。

举个例子,假定有一个链表a1->a2->...->an->b1->b2->...->bn,你想将其重新排列成a1->b1->a2->b2->...->an->bn。另外,你不知道该链表的长度(但确定其长度为偶数)。

你可以用两个指针,其中p1(快指针)每次都向前移动两步,而同时p2只移动一步。当p1到达链表末尾时,p2刚好位于链表中间位置。然后,再让p1p2一步步从尾向头反向移动,并将p2指向的节点插入到p1所指节点后面。

递归问题

许多链表问题都要用到递归。解决链表问题碰壁时,不妨试试递归法能否奏效。这里暂时不会深入探讨递归,后面会有专门章节予以讲解。

当然,还需注意递归算法至少要占用O(n)的空间,其中n为递归调用的层数。实际上,所有递归算法都可以转换成迭代法,只是后者实现起来可能要复杂得多。