方法一:
一共使用2个指针,分别是p和q
指针p每走一步就停下来,保存走到的位置
指针q每次都要从头结点开始走
这样走到某个位置的时候
2个指针走的步数会不一样
这样就说明有环
例如下图所示的循环链表
如果p走到6,再走一步就走到3
可是q走到3只需要走2步
到达相同的位置,但是步数不一样
这样就说明有环
方法二:快慢指针法
定义2个指针p,q
每次q走2个位置,p走1个位置,
如果有环的话
q走的快,就会绕回来,
而p走的慢,就会被绕回来的q追上
如果没有环的话,p和q永远不会相撞
#include <stdio.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*LinkList;
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node));
if(!(*L))
{
return ERROR;
}
(*L)->next=NULL;
return OK;
}
int ListLength(LinkList L)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
p=p->next;
}
return i;
}
void CreateListHead(LinkList *L,int n)//头插法创建循环链表
{
LinkList p;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));//malloc()这个函数分配完内存空间后
//就会返回指向这个内存空间的指针
(*L)->next=NULL;
for(i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;//随机产生100以内的数字
p->next=(*L)->next;
(*L)->next=p;//生成单链表(不是循环的),头插法就是把每次新生成的结点插到
//头结点之后,之前已经插入的结点应当往后挪
//所以把p的下一个结点的地址放到新生成的结点L的next域内
//然后断开之前的链,把L的地址放到p的next域内
/*这里的LinkList其实和int,char是一个意思
只是是自己新定义的一种类型而已
LinkList在定义的时候就定义成了一种指针
特别指向链表的头结点
而Node只是定义成了一种数据结构
但是Node * s和LinkList s本质上没有区别
都是指针,只是说法不同而已
*/
}
}
void CreateListTail(LinkList *L,int n)
{
LinkList p,r;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));
r=*L;
for(i=0;i<n;i++)
{
p=(Node *)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r->next = (*L)->next->next;
}
//比较步数的方法
int HasLoop1(LinkList L)
{
LinkList cur1 = L;
int pos1=0;
while(cur1)
{
LinkList cur2 = L;
int pos2 = 0;
while(cur2)
{
if(cur2 == cur1)
{
if(pos1 == pos2)
{
break;
}
else
{
printf("环的位置在第个结点处.\n\n",pos2);
return 1;
}
}
cur2 = cur2->next;
pos2++;
}
cur1 = cur1->next;
pos1++;
}
return 0;
}
//利用快慢指针的方法
int HasLoop2(LinkList L)
{
int strp1 = 1;
int step2 = 2;
LinkList p = L;
LinkList q = L;
while(p !=NULL && q !=NULL)
{
p = p->next;
if(q->next !=NULL)
{
q =q->next;
printf("p:%d q:%d\n",p->data,q->data);
if(p == q)
return 1;
}
return 0;
}
}
int main()
{
LinkList L;
Status i;
char opp;
ElemType e;
int find;
int tmp;
i=InitList(&L);
printf("初始化L后:ListLenfth(L)=%d\n",ListLength(L));
printf("\n1.创建有环链表(尾插法)\n2.创建无环链表(头插法) \n3.判断链表是否有环 \n0.退出 \n");
while(opp!='0')
{
scanf("%c",&opp);
switch(opp)
{
case '1':
CreateListTail(&L,10);
printf("成功创建有环链表(头插法)\n");
printf("\n");
break;
case '2':
CreateListHead(&L,10);
printf("成功创建无环链表(头插法)\n");
printf("\n");
break;
case '3':
printf("方法一:\n\n");
if(HasLoop1(L))
{
printf("结论:链表有环\n\n\n");
}
else
{
printf("结论:链表无环\n\n\n");
}
printf("方法二:\n\n");
if(HasLoop2(L))
{
printf("结论:链表有环\n\n\n");
}
else
{
printf("结论:链表无环\n\n\n");
}
printf("\n");
break;
case '0':
exit(0);
}
}
}