参考很多资料,也做了点题目,在这里小汇总一下。有不到位的地方,恳请指出,谢谢!
描述:
给定一个链表头指针为head,针对这个链表有如下问题:
1、如何判断这个链表有没有环?
2、环的长度如何计算?
3、怎么找到环开始的地方?
解决思路:
1、两种思路,第一种:用一个map保存节点的地址,从前往后扫,如果某个节点的地址出现了两次,则说明有环。这种思路实现起来也很快,但不适合解决第二个问题,空间复杂度比较大。在这里不做主要讨论。如果笔试或者面试碰到,一般考的是这里的第二种方法。取两个指针,slow和fast,开始都指向head,slow每次走一步,fast每次走两步,如果这两个指针最后能够相遇,则说明有环。它的一个简单实现,参考:http://blog.csdn.net/hale1007/article/details/22226133
2、两个指针在环内相遇之后,再继续走,到第二次再相遇,慢指针走了一圈,快指针走了两圈,用一个计数器记住慢指针的走的步数就可以得到环的长度。
3、如下图:
slow和fast在p的位置相遇,这个时候slow走的距离 s = N + M2,fast走的距离为2s,因为二者相遇,肯定有fast在环内打转,假设fast转了n圈(其中n>=1),
所以就有 2s = N + n(M1 + M2) + M2;综合这两个等式,于是就有,N + M2 = n(M1 + M2),也就是N = (n-1)(M1+ M2) + M1
有了这个结论,让slow和fast在p相遇之后,让fast指向链表开头,从这个时候开始fast每次只走一步,slow继续在环内走,还是一次一步,两个指针再次相遇的地方就是环开始的地方,注意这个时候,slow在环内走了n-1圈。LeetCode上有这个题目,参考:http://blog.csdn.net/hale1007/article/details/22225483
一个完整实现的代码:
#include <iostream>
using namespace std;
struct ListNode
{
int data;
ListNode *next;
};
ListNode *build_list_with_cycle(int a[], int len, int begin) // begin为环开始的地方
{
ListNode *head = nullptr, *p = nullptr, *q = nullptr; // 用q记录begin指向的位置,即环开始的地方
for (int i = 0; i < len; i++)
{
ListNode *tmp = new ListNode();
tmp->data = a[i];
if (i == 0)
{
head = p = tmp;
continue;
}
if (i == begin)
q = tmp;
p->next = tmp;
p = p->next;
}
p->next = q; // 最后一个节点指针指向之前记录的begin 的位置,让链表成环
return head;
}
bool has_cycle(ListNode *head)
{
ListNode *fast = head, *slow = head; // 快慢指针开始都指向第一个节点
while (slow && fast && fast->next)
{
fast = fast->next->next; // 快的每次走两步,慢的每次走一步
slow = slow->next;
if (slow == fast) // 快慢指针相遇,则说明有环
return true;
}
return false;
}
int cycle_length(ListNode *head) // 在已经确定有环的情况下求环的长度
{
ListNode *fast = head, *slow = head;
int length = 1; // 环长度至少为1
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
fast = fast->next->next;
slow = slow->next;
while (fast != slow)
{
length++; // slow每走一步,长度加1
fast = fast->next->next;
slow = slow->next;
}
break;
}
}
return length;
}
ListNode *find_cycle_begin(ListNode *head) //如果有环,返回环开始的地方,如果没有,返回nullptr
{
ListNode *fast = head, *slow = head;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
fast = head;
while (fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow; // or return fast
}
}
return nullptr; // 找不到环,则返回空
}
int main()
{
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
int len = sizeof(a) / sizeof(int);
int begin;
cin >> begin;
ListNode *head = build_list_with_cycle(a, len, begin);
ListNode *cycle_begin = nullptr;
int pre_length;
if (has_cycle(head))
{
cout << "has cycle" << endl;
cout << "cycle length: " << cycle_length(head) << endl;
cycle_begin = find_cycle_begin(head);
cout << "the data of the cycle begin " << cycle_begin->data << endl;
}
else
cout << "no cycle" << endl;
}