判断链表是否有环并找到进入环的那个结点
单链表的三种情况:
1,尾结点接到头结点上 (循环链表)
2,尾结点接到链表中间(数字6形状)
3,无环,存在结点指向null
思想:定义两个指针,fast指针跑的速度是slow的二倍。
1,如果两指针相遇,那么就有环(有环的话,他们两都会一直在环里跑,而fast的速度是slow的两倍,所以会相遇)
2,如果fast指针不跑了,说明遇到了null,即表示没有环。
typedef int ElemType;
typedef struct _Node
{
ElemType data;
struct _Node *next;
}LNode, *LinkList;
static LinkList Have_Ring(LinkList list)
{
LinkList fast = list->next;
LinkList slow = list;
while (fast)
{
if (fast == slow) return fast;
else
{
if ((fast->next)==NULL) return NULL ;
else
{
fast = fast->next->next;
slow = slow->next;
}
}
}
return NULL;
}
这个还是比较好理解的~因为后面推导环开始的位置需要用到两指针相遇的地方,所以我们这里用返回指针的函数。
找到入环的结点
假设两个指针在下图所示位置相遇;
我们在上面定义两个指针的时候,fast的指针的速度是slow指针的两倍,在两指针相遇的时候,花费时间是相同的。
所以我可以的得到以下等式
2(x+y)=x+y+n(z+y)
化解:
x=(n-1)(y+z)+z
这个式子也可以写成:
x=n(y+z)+z
(因为在数学角度,n和n-1是没多大区别的,区别在于你n取多少值)
那么这个式子是什么意思呢?
我们再设立两个指针(p和q),一个(p)放在头结点的位置,一个(q)放在fast和slow相遇的结点;
两个指针同时开始走,速度相同,那么当这两个指针相遇的时候,处于的结点位置就是入环的结点。
可能q会围绕着环转很多圈,也有可能第一次到入环结点的时候,就和p相遇了。
这里不用考虑p指针入环,还和q指针没有相遇的情况,由于我们得到的那个式子,所以这个情况是不可能存在
具体实现如下:
int Find_Entrynode(LinkList list)
{
LinkList p = list ->next;
LinkList q = Have_Ring(list);
int count=0; //用于表示入环结点的位置
while(p!=q)
{
p = p->next;
q = q->next;
count++;
}
return count;
}