环形链表的题目:
第一个是判断里面是否有环;
第二是找环在那里,返回环的位置。
第一个题目:判断环
思路:
我们可以定义一个快慢指针,我们的快指针一次走两步,满指针一次走一步,当我们的快慢指针都指向同一个地方时就是我说明我们的链表有环。
解析:
当我们的指针都入环了,假设他们的距离差是n那每走一次,两个指针的距离就减少1,南无最后不管n是奇数还是偶数就都会相遇,
代码:
这个题目的代码很简单:
typedef struct ListNode Node;
bool hasCycle(struct ListNode *head) {
Node* slow = head;
Node* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
若走更多的步数行不行?
那这里还有一个疑问我们走三步.四步等等可不可以呢?
这里就需要我们去进行计算了,当我们一次走三步时,当他们入环的时候呢,他们每次距离就缩小两步;那我们这里就有一个问题了,当他们之间的差距是偶数时,那么在第一圈的时候就会追上,但是当他是奇数的时候就会他们直间的距离就会变成一个-1,快指针就会超过慢指针,刚刚好错过了。
那么继续发生第二次追及,那么我们来思考一下第二次追击会不会追上,他们两个的距离就会变成环的长度减一,如果我们的环的长度是C,如果C-1是奇数那就永远追不上,C-1是偶数那就可以追上。
我们就要来验证一下这个第二种可能出现吗?
我们发生第二种情况的是当他们都入环的时候距离位N,N这个时候是奇数,而且我们的C-1也是奇数,即C是偶数,这种情况存不存在。
我们可以通过他们的路程的来写出一个等式,我们等式的左边是一个偶数那么我们的右边计算的结果也要是偶数,那我们偶数减偶数就是偶数,奇数减奇数也偶数。
所以当我们的N是奇数时C也是奇数,不会出现我们的N是奇数,C是偶数。
所以他最后还是会追上的,只是不会在第一圈追到,最后还是会追到的。
同理当我们的快指针走4步的时候,就会出现C-1和C-2这两种情况。C-2的也和我们的一样
结论:
第二个题:找环
思路:
我们也是利用快慢指针来写,我们先来找到我们快慢指针相遇的地方,然后我们在一个指针从相遇的地方开始走,一个从头节点开始走,两个指针同时开始走,他们再次相遇的地方就是入环的节点
解析
我们的环的长度为C;我们入环前的长度是L,当我们两个指针相遇时,假设在环里面走了N,那么我们再让一个从开头走,一个从相遇的节点走,我们可以通过等式来证明:
我们不知道我们入环之前的距离是多少,可能会很长,也可能会很短,所以可能在他们相遇之前,那个快指针就走了x圈。
这里我们就能得出我们最后让两个指针,一个从相遇点开始走一个从头结点开始走,最后他们相遇的就是我们的入环节点。
代码:
typedef struct ListNode Node;
struct ListNode *detectCycle(struct ListNode *head) {
Node* slow = head;
Node* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
//走到相遇点
if(slow == fast)
{
// 求环的入口点
Node* meet = slow;
Node* start = head;
while(meet != start)
{
meet = meet->next;
start = start->next;
}
return meet;
}
}
return NULL;
}