引入
在正式学习双向循环链表的之前,我们先了解什么是循环链表、什么是双向链表。
循环链表
对于单链表,由于每个结点只存储了其后继结点的指针,到了尾标志(NULL)就停止了向后链的操作,这样,当中某一结点就无法找到它的前驱结点了,就像我们平时所说的不能回到从前。
比如,你是一业务员,家在上海。需要经常出差,行程就是上海到北京一路上的城市,找客户谈生意或分公司办理业务。你从上海出发,乘火车路经多个城市停留后,再乘飞机返回上海,以后,每隔一段时间,你基本还要按照这样的行程开展业务,如下图所示:
有一次,你先到南京开会,接下来要对以上的城市走一遍,此时有人对你说,不行,你得从上海开始,因为上海是第一站。你会对这人说什么?神经病。哪有这么傻的,直接回上海根本没有必要,你可以从南京开始,下一站蚌埠,直到北京,之后再考虑走完上海及苏南的几个城市。显然这表示你是从当中一结点开始遍历整个链表,这都是原来的单链表结构解决不了的问题。
事实上,把北京和上海之间连起来,形成一个环就解决了前面所面临的困难。这就是我们现在要讲的循环链表。
将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circularlinkedlist)。
从刚才的例子,可以总结出,循环链表解决了一个很麻烦的问题。如何从当中一个结点出发,访问到链表的全部结点。为了使空链表与非空链表处理一致,我们通常设一个头结点,当然,这并不是说,循环链表一定要头结点,这需要注意。循环链表带有头结点的空链表如图所示:
其实循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断 p->next是否为空,现在则是 p->next不等于头结点。
在单链表中,我们有了头结点时,我们可以用 O(1)的时间访问第一个结点,但对于要访问到最后一个结点,却需要 O(n)时间,因为我们需要将单链表全部扫描一遍。
有没有可能用 0(1)的时间由链表指针访问到最后一个结点呢?当然可以。不过我们需要改造一下这个循环链表,