将单链表中终端节点的指针由空指针改为指向头节点,就使整个单链表形成一个环, 这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list)
循环链表解决了一个很麻烦的问题。如何从当中一个节点出发,访问到链表的全部节点。
为了使空链表与非空链表处理一致,我们通常设一个头节点,当然,这并不是说, 循环链表一定要头节点,这需要注意。
空循环链表:
非空循环链表:
其实循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断 p->next 是否为空, 现在则是 p->next 不等于头节点,则循环未结束。
在单链表中,我们有了头节点时,我们可以用 O(1) 的时间访问第一个节点,但对于要访问到 最后一个节点,却需要 O(n) 时间,因为我们需要将单链表全部扫描一遍。
有没有用 O(1) 的时间由链表指针访问到最后一个节点呢?当然可以。
不过我们需要改造一下这个循环链表,不用头指针,而是用指向终端节点的尾指针来表示循环链表, 此时查找开始节点和终端节点都很方便了。
从上图中可以看到,终端节点用尾指针 rear 指示,则查找终端节点是 O(1), 而开始节点,其实就是 rear->next->next,其时间复杂度也为 O(1)。
举个程序的例子,要将两个循环链表合并成一个表时,有了尾指针就非常简单了。 比如下面的这两个循环链表,它们的尾指针分别是 rearA 和 rearB:
要想将它们合并,只需要如下的操作即可:
1 | p = rearA->next; // 保存 A 链表的头节点 |