算法思想:
因为是单链表,那么单链表内部成环的原因只会是尾结点的指针域指向链表中的某一个结点。那么单链表成环的形状只有下面三种形式:
不管单链表形状如何,处理的方法都是一致的。
- 建立单链表,因为单链表的建立无要求,就采用尾插法建立单链表。
- 设置链表内部有环,选取链表内某一个结点位置,尾结点的指针域指向该结点。此时链表内部成环。
- 判断单链表是否有环,处理的方法:自定义两个结点指针:ppre和pcur,初始时,ppre指向首结点,pcur指向链表的第二个结点,这样错开是避免后续判断出错。用一个循环控制ppre和pcur向后偏移。设置一个信号变量sign,每次循环就先判断这两个指针是否指向同一个结点,若是,sign为1,跳出循环,输出单链表中有环;否则,ppre向后偏移一个位置,pcur向后偏移两个位置。但凡链表内部有环,ppre和pcur必然出现重合,指向同一个结点。若是内部无环,则循环的条件需要考虑输入结点的奇偶个数问题。
- 解除链表内部的环,即将尾指针的下一个指向置空。
- 再次判断链表内部是否有环。
假设链表内部有环,ppre和pcur两个结点指针指向变化如下:
案例运行效果:
此时链表内各结点状态如下:
当解除链表内部环后,再次判断链表内部是否有环
此时链表内各结点状态如下:
完整代码如下:
#include<stdio.h>
#include<stdlib.h>
#define N 3
typedef struct linklist {
int data;
struct linklist *next;
}list,*plist;
void list_create(plist *pphead,plist *pptail,int key)
{
plist pnew = (plist)calloc(1, sizeof(list));
pnew->data = key;
if (NULL == (*pphead))
{
*pphead = pnew;
*pptail = pnew;
}
else {
(*pptail)->next = pnew;
*pptail = pnew;
}
}
void setring(plist p, plist *pptail, int flag) //设置单链表内部第n个结点位置开始成环
{
int count = N-1;
if (flag)
{
while (count > 0)
{
p = p->next;
count--;
}
(*pptail)->next = p;
}
}
void judgering(plist phead) //判断单链表内部是否有环
{
plist ppre = phead, pcur = phead->next;
int sign=0;
while (pcur && pcur->next) //考虑无环链表输入结点的奇偶个数
{
if (ppre == pcur)
{
sign = 1;
break;
}
ppre = ppre->next;
pcur = pcur->next->next;
}
if (sign)
printf("单链表内部有环\n");
else
printf("单链表内部无环\n");
}
void removering(plist *pptail,int signal) //解除单链表内部的环
{
if (signal)
(*pptail)->next = NULL;
}
int main()
{
plist phead = NULL, ptail = NULL;
int key,flag,signal;
printf("是否预设链表有环:【是:1;否:0】");
scanf("%d", &flag);
while (scanf("%d", &key) != EOF)
{
list_create(&phead, &ptail, key);
}
setring(phead, &ptail, flag);
judgering(phead);
printf("是否解除链表内部的环:【是:1;否:0】");
scanf("%d", &signal);
removering(&ptail, signal);
judgering(phead);
system("pause");
}