线性表--顺序表--循环链表(五)
一.介绍
单循环链表,简称循环链表,是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。和单链表唯一的区别就是,尾结点指向头结点,因此循环链表中没有NULL指针。而涉及遍历操作时,其终止条件就不再是像非循环链表那样判别p或p->next是否为空,而是判别它们是否等于某一指定指针,如头指针或尾指针等,在单链表中,从一已知结点出发,只能访问到该结点及其后续结点,无法找到该结点之前的其它结点。而在单循环链表中,从任一结点出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。
二.图示
单链表是这样的:
循环链表是这样的:
二.代码实现
1.定义链表
上面已经说过,循环链表和单链表唯一的不同就在于尾结点指向头结点,所以链表的定义和单链表一样。
typedef struct list
{
int data;
struct list * next;
}list;
2.初始化头结点
这里初始化头结点也和单链表一样,没有什么可说的。
list * InitListHead()
{
list *Phead = (list *)malloc(sizeof(list));//创建头结点
Phead->data = 0; //该变量可存放该链表长度
Phead->next = Phead;
return Phead; //创建后返回该头指针
}
3.循环链表结点初始化
void CreateCLinkList(list ** CL,int n)
{
//利用尾插法建立循环链表CL
list* rear, *s;
rear = (*CL); //rear指针动态指向链表的当前表尾,其初值指向头节点
for (int i = 1; i <= n; i++)
{
s = (list *)malloc(sizeof(list));
s->data = i;
rear->next = s;
rear = s;
}
rear->next = (*CL);//让最后一个节点的next链域指向头节点。
}
至于循环链表的增删查改,都同单链表一样,我们就不在多赘述,只是在遍历这块有一定小猫腻。
4.循环链表的遍历(重点)
如果是这样:
void printList(list * Phead)
{
list P=Phead;
while(P->next!=Phead)
{
printf("%d",P->data );
P=P->next;
}
}
上述代码你会发现,该链表的最后一个结点不会被输出,当p->next=Phead的时候,循环被停止。 这时的是不是又抖机灵想着把while(P->next!=Phead)改为while(P!=Phead)???你想啥呢,这连循环都不会去进去。都 是头结点惹的祸,第一种差一个结点,第二章根本就不去,解决方法是,我们可以进循环之前,把头节点数据先输出,这样显然有点麻烦,代码看着有点笨拙,这时我们就应该想到被我们丢弃的do—while循环。
void printList(list * Phead)
{
list P=Phead;
do{
printf("%d",P->data );
P=P->next;
}while(P!=Phead);
}
看,do—while天然的无条件执行一次,完美的为我们解决了这个难题。
5.如何判断是否为循环链表(重点)
首先来说说这种循环链表:
这种就比较简单了,只需要判断就没有指向NULL的指针,再看看头结点是不是重复出现,如果重复出现那一定就是循环链表了。 代码如下:
void JudgeList(list * Phead)
{
list * P=Phead;
while(P!=NULL)
{
P=P->next;
if(P==Phead)
return true;
}
return false;
}
到这里,你是不是觉得就完了,那就错了,现实偏偏不是这样,偏偏就有下面这种循环链表的存在(你说气不气):
如果再使用上面的代码,函数传入一个头结点,显然没有用了,所以,快慢指针登上历史舞台。 快慢指针是利用不同的步长,然后让快慢指针相遇,则可以证明链表是一个环,也就是循环指针,就好像校园的田径场,跑的快的,最终会追上跑的慢的,所以对于上面这种循环链表,我们可以采用这种方法来判断是否为循环链表。 代码实现:
void JudgeList(list * Phead)
{
list * fast;
list * slow;
fast=slow=Phead;//两个指针在同一起跑线
while(1)
{
if(fast==slow||fast->next==slow) //快的跑一圈又追上慢的,或者慢指针跑到前面
return true;
else
{
slow=slow->next;
fast=fast->next->next; //快指针比慢指针多走一步
}
}
}
若有错误,欢迎批评指正,欢迎讨论。 每文一句:超越自己,向自己挑战,向弱项挑战,向懒惰挑战,向陋习挑战。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法