环形链表 试试这题
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
解题思路:带环的链表让人头疼的地方就是不能遍历链表,因为如果你遍历链表就会链表死循环,这是我们就只有有双指针来解决这个问题,定义两个指针fast和slow指针,fast指针每次走两步,slow指针每次走一步,当两个指针同时从head头指针开始遍历,因为fast指针移动比较快先进入环里面(如果有的话)
,然后因为fast指针每次都比slow指针快一步,所以在里面形成了追击问题,每次再环里面两个指针的差距都会减少一,直到最后相遇,如果中间fast指针或fast->next为NULL,则直接判为不带环
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode Node;
bool hasCycle(struct ListNode *head) {
//如果只有一个结点或者头结点为空则一定不是带环链表
if(head == NULL || head->next == NULL)
return false;
Node* fast = head;
Node* slow = head;
//判断fast指针或fast->next指针是否为空
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(slow == fast)//如果slow和fast指针相遇,那么就是带环指针
return true;
}
return false;//如果跳出循环,说明链表不带环
}
那为什么sloe指针走一步fast指针走两步呢?如果fast指针走3,4,5…N步行不行呢?
下证:
因为fast指针和slow指针都会进环的(如果有环的话)那就假设指针都已经进环了,
这时fast指针其实就是在在这个环里在追赶slow指针,只要fast追赶slow超了一圈,那就追上了,,假设fast和slow相差N步,(fast每次2步,slow每次1步)每走一次他们的距离就减少1,距离变化:N-1,N-2,N-3…1,0。最后变成0,但是要是fast每次走3步呢?每次距离较少2,N-2,N-4…如果环中的节点数为奇数个最后指针一步的时候就变成-1距离了,那就又相差一步了,这样下去又变成死循环了,如果是4步也是同样的道理,每次快3步,结点数为偶数,那也会死循环,所以总结就是相差距离N必须是每次减少距离的倍数(相加距离N % (fast每次步数 - slow每次步数) == 0)才不会死循环,又因为1是任何数的因数所以每次fast指针不slow指针快1步。
环形链表 II(在带环链表的入口点)试试这题
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode Node;
struct ListNode *detectCycle(struct ListNode *head) {
//先找到相遇点
Node* fast = head;
Node* slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
break;
}
//判断链表是否带环,如果fast或fast->next为NULL,说明不带环
if(fast == NULL || fast->next == NULL)
return NULL;
//如果带环则,让fast指针或slow指针和head指针一起走,他们相遇的点就是换的入口点
slow = head;
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}
head到入环口距离k,入环口到相遇位置距离x,环的周长为C.
slow指针走距离,fast指针走k+x+NC距离,N是一个系数,环越大fast在和slow指针先相遇前绕环的圈数越少,环越小绕环次数越大(N越大),fast = 2*slow,
2(x+k)= k+x+NC
x+k = NC
k = NC-x
k = (N-1)C + C - x
所以头指针到入环位置等于相遇位置到入环位置,因为中间fast指针已经饶了N-1圈环长所以所以还是是相遇位置,最特殊的时候就是当N=1时,两个位置相等,这样通过fast指针和head指针同时走相遇时就可以找到入口点