将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,
    这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list)。

    循环链表解决了一个很麻烦的问题。如何从中间的一个结点出发,访问到链表的全部结点。

    为了使空链表与非空链表处理一致,我们通常设一个头结点,
    当然,这并不是说,循环链表一定要头结点,这需要注意。

    • 循环链表带有头结点的空链表。

    如图3-13-3所示:
    image.png
    图3-13-3

    • 对于非空的循环链表就。

    如图3-13-4所示:
    image.png
    图3-13-4

    其实循环链表和单链表的主要差异就在于循环的判断条件上,

    • 原来是判断p->next是否为空,
    • 现在则是p->next不等于头结点,则循环未结束。

    在单链表中,我们有了头结点时,我们可以用O(1)的时间访问第一个结点,但对于要访问到最后一个结点,却需要O(n)时间,因为我们需要将单链表全部扫描一遍。
    有没有可能用O(1)的时间由链表指针访问到最后一个结点呢?当然可以。

    不过我们需要改造一下这个循环链表,
    不用头指针,而是用指向终端结点的尾指针来表示循环链表(如图3-13-5所示),
    此时查找开始结点和终端结点都很方便了。image.png图3-13-5
    从上图中可以看到,终端结点用尾指针rear指示,

    • 则查找终端结点是O(1),
    • 而开始首结点,其实就是rear->next->next,其时间复杂也为O(1)。

    举个程序的例子,要将两个循环链表合并成一个表时,有了尾指针就非常简单了。比如下面的这两个循环链表,它们的尾指针分别是rearA和rearB,如图3-13-6所示。
    image.png
    图3-13-6
    要想把它们合并,只需要如下的操作即可,如图3-13-7所示。
    image.png
    图3-13-7

    1. /* 保存A表的头结点,即① */
    2. p = rearA->next;
    3. /*将本是指向B表的第一个结点首结点(不是头结点) */
    4. rearA->next = rearB->next->next;
    5. /* 赋值给reaA->next,即② */
    6. q = rearB->next;//保存B表的头结点
    7. /* 将原A表的头结点赋值给rearB->next,即③ */
    8. rearB->next = p;
    9. //rearB->next = rearA->next;
    10. //前后的->next有不同的意义,前式是(B表的尾结点的)指针域,后式是(A表尾结点指向的头结点的)地址,因为赋值符号前式的值是被后式的值覆盖,所以前式可以视为空地址的指针域,后式则不能
    11. /* 释放q */
    12. free(q);