【基础知识】| 作者 / Edison Zhou
这是恰童鞋骚年的第192篇原创文章
上一篇介绍了双链表的实现,这一篇我们来学习下循环链表。
1循环链表的基础
循环链表的节点结构
循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断p.next是否为空,现在则是p.next不等于头结点,则循环未结束。
循环链表的O(1)访问时间
在单链表中,有了头结点,我们可以在O(1)时间访问到第一个节点,但如果要访问最后一个节点却需要O(n)的时间,因为我们需要对整个链表进行一次遍历。在循环链表中,我们可以借助尾节点来实现,即不用头指针,而是用指向终端结点的尾指针来表示循环链表,这时候无论是查找第一个节点还是最后一个节点都很方便,可以控制在O(1)的时间内,如下图所示。
从上图中可以看到,终端结点用尾指针(tail)指示,则查找终端结点是O(1),而开始结点,其实就是tail.Next,其时间复杂也为O(1)。由此也可以联想到,在合并两个循环链表时,只需要修改两个链表的尾指针即可快速地进行合并。
2循环链表的实现
Talk is cheap, show me the code。多说无益,开始用C#来实现一个吧。
循环链表的节点定义实现
public class CirNode{ public T Item { get; set; } public CirNode Next { get; set; } public CirNode(){ } public CirNode(T item){ this.Item = item; }}
这里跟单链表的节点定义实现并无区别。
循环链表的新节点插入
public void Add(T value){ CirNode newNode = new CirNode(value); if (this.tail == null) { // 如果链表当前为空则新元素既是尾头结点也是头结点 this.tail = newNode; this.tail.Next = newNode; this.currentPrev = newNode; } else { // 插入到链表末尾处 newNode.Next = this.tail.Next; this.tail.Next = newNode; // 改变当前节点 if (this.currentPrev == this.tail) { this.currentPrev = newNode; } // 重新指向新的尾节点 this.tail = newNode; } this.count++;}
首先,这里的currentPrev字段是使用了前驱节点来标识当前节点,如要获取当前节点的值可以通过currentPrev.Next.Item来获得。其次,在最后将尾节点指针指向新插入的节点。
3小结
本文介绍了循环链表的定义及如何实现循环链表的节点与新节点插入。下一篇,我们将要继续学习循环链表的节点移除及完整DEMO测试。
4参考资料
程杰,《大话数据结构》
陈广,《数据结构(C#语言描述)》
段恩泽,《数据结构(C#语言版)》