问题一:判断单链表是否带环?
这个问题比较容易解决,我们只需要使用快慢指针的方式便可以解决该问题,快指针(一次走两步)、慢指针一次走一步,如果链表带环这两个指针肯定会相遇,由于快指针走了两步,所以还是要考虑到节点个数是奇数还是偶数,所以【fast->next != NULL】也是要判断的!
//不带环返回NULL,带环返回相遇点
pNode CheckCycle(pList plist)
{
pNode fast = plist;
pNode slow = plist;
if (plist == NULL)
return NULL;
while ((fast != NULL) && (((fast->next) != NULL)))
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
return fast;
}
return NULL;
}
问题二:求链表的环的长度,参数为相遇点
从相遇点的下一个节点开始计数,直到重新回到相遇点即是换的长度!
//求环的长度,参数为相遇点
int GetCircleLength(pNode meet)
{
pNode cur = NULL;
int len = 1;
assert(meet != NULL);
assert(meet->next != NULL);
cur = meet->next;
while (cur != meet)
{
len++;
cur = cur->next;
}
return len;
}
求入口点位置,参数为相遇点
这个相对前面两个来说比较难想,假设不带环的部分长度是x,从入口点到两个指针的相遇点的长度为y,环的长度为L,我们可以得到如图所示的公式
即慢指针走的步数乘以2就是快指针走的距离,我们可以得出x+y是个常数K*L(也就是k个环的长度),则x总是环的长度的倍数减去y,也就是说慢指针一个从链表的起始位置走,另一个慢指针从相遇点开始走,它们总会在入口点相遇!
//求入口点,参数为相遇点
pNode GetCycleEntryNode(pList list, pNode meetNode)
{
pList cur = list;
if (list == NULL)
return NULL;
if (meetNode == NULL)
return NULL;
while (cur != meetNode)
{
cur = cur->next;
meetNode = meetNode->next;
}
return cur;
}
测试代码
void test()
{
int i = 0;
pList plist = NULL;
pNode pos = NULL;
pNode entrance = NULL;
InitList(&plist);
for (i = 1; i <= 5; i++)
{
PushBack(&plist, i);
}
//带环
Find(plist, 5)->next = Find(plist, 3);
pos = CheckCycle(plist);
if (pos != NULL){
printf("带环,相遇点为 = %d\n", pos->data);
printf("环的长度是:%d\n", GetCircleLength(pos));
entrance = GetCycleEntryNode(plist, pos);
printf("环的入口点是:%d\n", entrance->data);
}
else
{
printf("不带环\n");
}
}