leetcode中刷题的时候遇到环形链表的问题,然后网上查找一下,对环形链表的问题做下记录。
首先看一张环形链表的图,如图所示:
1.判断一个链表是否有环
a.采用hashset方式,遍历链表,每次将链表节点存放到set中,如果如果遍历过程中中发现了set对应的值,则认为链表存在环,代码如下:
1 var hasCycle = function(head) { 2 const set = new Set(); // 通过创建一个set, 向set中添加listnode,如果每次循环的时候已经发现set中存在listnode,则认为有环 3 while (head !== null) { 4 if (set.has(head)) { 5 return true; 6 } else { 7 set.add(head); 8 } 9 head = head.next; 10 } 11 return false 12 }
此方式由于创建了set,消耗了多余的空间。
b.采用快慢指针方式,设置一个fast指针,每次移动两步,一个slow指针,每次移动一步,一直循环,如果出现fast == slow重合情况,则认为链表有环,代码如下:
1 var hasCycle = function(head) { 2 if (!head || !head.next) { 3 return null; 4 } 5 6 let fast = head; 7 let slow = head; 8 while (fast.next && fast.next.next) { 9 slow = slow.next; 10 fast = fast.next.next; 11 if (slow == fast) { // 存在相遇的情况,则链表存在环 12 return slow; 13 } 14 } 15 return null; 16 }
2.计算链表环的长度:
在环上相遇后,记录第一次相遇点为B,之后指针slow继续每次走1步,fast每次走2步。在下次相遇的时候fast比slow正好又多走了一圈,也就是多走的距离等于环长。
设从第一次相遇到第二次相遇,设slow走了len步,则fast走了2*len步,相遇时多走了一圈:
环长=2*len-len。
执行第一步判断是否存在环,得到第一次相遇节点位置,则fast与slow再次相遇的时候,即得到环长
1 const meetingNode = hasCycle(head); 2 var getLength = function (meetingNode){ 3 let length = 0; 4 let fast = meetingNode; 5 let slow = meetingNode; 6 while (true) { 7 fast = fast.next.next; 8 slow = slow.next; 9 length += 1; 10 if (fast === slow) { 11 break; 12 } 13 } 14 return length; 15 }
3.查找链表环的起点:
假设起点Head到环起点C的长度为LenA;开始到第一次相遇,移动了t步;环的长度为R;环的起点到二者第一次相遇位置的距离为L1,二者相遇的时候快指针已经在环上转了n圈。
则有如下等式成立: 慢指针(slow)移动的距离=L3 + L1; 即 t = L3 + L1;
快指针移动的距离=L3 + n * R + L1; 即 2t = L3 + n * R + L1;
所以 2*(L3 + L1) = L3 + n*R + L1;
即 L3 = n * R - L1;
上面的等式可以这样理解,如果慢指针从链表起点开始移动,快指针从环起点开始移动,当慢指针从起点走到环起点的时候,快指针一直在环上移动,而且还差x步就移动了n圈。进而可以得到结论,如果慢指针从链表起点开始移动,快指针从二者第一次相遇点开始移动,当慢指针到达环起点的时候,快指针也正好达到环起点,即二者指向相同的节点。
1 var detectCycle = function(head) { 2 if (!head || !head.next) { 3 return null; 4 } 5 6 let fast = head; 7 let slow = head; 8 let pos = head; 9 10 while (fast.next && fast.next.next) { 11 slow = slow.next; 12 fast = fast.next.next; 13 14 if (slow == fast) { 15 // 头结点至环开始的距离与快慢指针相遇点至环开始的距离是一致的; 16 // 当快慢指针相遇后,头结点与慢指针继续向前移到,头结点指针与慢指针会在环开始位置相遇 17 while (slow != pos) { 18 slow = slow.next; 19 pos = pos.next; 20 } 21 return pos; // 此时的pos即为环的开始节点 22 } 23 } 24 25 return null; 26 };
如有错误,🙏谢谢指正。