【LeetCode】关于链表的环、相交等问题
1、链表环形
LeetCode上关于环形链表有两个题:
- Q141–easy
- 内容:判断一个链表是否有环。
- Q142–medium
- 内容:判断一个链表是否有环,并返回链表环形开始的第一个节点。
题目141思路分析:
判断一个链表是否有环,可以使用“快慢指针”的方法,快指针一次走两格即fast = fast.next.next
,慢指针一次走一格即slow = slow.next
。可以分为两种情况:
-
无环:即遍历到最后,肯定是快指针
fast == null
-
有环:快指针先进入环,慢指针后进入。并且可以确定,快慢指针相遇时,慢指针没有走完一圈或刚好走完一圈。
因为在同一个环中fast和slow之间的距离不会大于环的长度,并且两者的距离一直在逐渐减小。
- 开始的时候,slow = 3 , fast = 0;
- slow = 2,fast = 2 -->刚好是在入口相遇。
注:左图为网图,本人只是在上进行了简单加工以及文字描述。
public boolean hasCycle(ListNode head) { if (head == null || head.next == null){ return false; } ListNode fast = head; ListNode slow = head; while (slow != null && fast.next != null){ slow = slow.next; fast = fast.next.next; //代表无环 if (fast == null){ return false; } //代表有环 if (slow == fast){ return true; } } return false; }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJNbcese-1639139015244)(C:\Users\qiuzt\AppData\Roaming\Typora\typora-user-images\image-20211210192443083.png)]
题目142思路分析:
相比上一个题目,增加了一个确定环开始的起点。
解题步骤分为两步:一是确定链表有环,二才是确定链表环的起始点。此题的难点是第二步。从题目141可以知道,当fast和slow相遇时候,slow还没有走完链表。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Px4MpCmp-1639139015245)(C:\Users\qiuzt\AppData\Roaming\Typora\typora-user-images\image-20211210193658155.png)]
从上面的分析知道,当fast和slow相遇时,slow还没有走完链表,假设fast已经在环内循环了n(1<= n)圈。假设slow走了s步,则fast走了2s步,又由于
fast走过的步数 = s + n*r(s + 在环上多走的n圈),则有下面的等式:
2*s = s + n * r ; (1)
=> s = n*r (2)
如果假设整个链表的长度是L,入口和相遇点的距离是x(如上图所示),起点到入口点的距离是a(如上图所示),则有:
a + x = s = n * r; (3) 由(2)推出
a + x = (n - 1) * r + r = (n - 1) * r + (L - a) (4) 由环的长度 = 链表总长度 - 起点到入口点的距离求出
a = (n - 1) * r + (L -a -x) (5)
集合式子(5)以及上图我们可以看出,从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。
因此我们就可以分别用一个指针(ptr1, prt2),同时从head与slow和fast的相遇点出发,每一次操作走一步,直到ptr1 == ptr2,此时的位置也就是入口点!
其实换句话说就是,当fast和slow再次相遇的时候,就是环的起始点了。别忘了慢指针得从头节点开始哦,上面说的就是,从相遇点起,接着往下走到环起始点的长度,和头节点到环起始点的长度是相等的。
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
boolean isLoop = false; //双指针找出有环或者无环
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
isLoop = true;
break;
}
}
if(isLoop){
slow = head;
while(fast != null && fast.next != null){
if(slow == fast){
return slow;
}
slow = slow.next;
fast = fast.next;
}
}
return null;
}
2、链表是否相交
LeetCode题目160,是一道简单题。
可以看到题目的最后一题的描述,即我们只要在返回结果之前,把链表回归原始结构就行。这个暗示有点明显!!!
这道题目前我会的有两种形式的解法:
-
把A链表的首尾相连,使AB两条链表组成一个环形链表,然后题目间接的就变成了Question142,但是需要注意返回结果之前,需要将链表恢复原来的结构。
-
类似于环形结构,只不过是AB两条链表先走自己的链表,走到最后,再走对方的链表,当然每次都的时候都需要比较结点是否相等。如果相等,那么说明相交。
可以这么理解,先自己走的路,然后再走对方的路,如果相交的话,肯定会在入口相遇。
假如A、B两个链表相交,相交链表的长度为Y,A不相交的链表长度为X,B不相交的链表长度为Z,按照上面A走的长度为X+Y+Z,B走的长度为Z+Y+X,两者相等。
public ListNode getIntersectionNode(ListNode headA, ListNode headB){ //令某单链表直接成环 ListNode curHeadA = headA; while(true){ if(curHeadA.next != null){ curHeadA = curHeadA.next; }else{ curHeadA.next = headA; break; } } //双指针问题 ListNode slow = headB; ListNode fast = headB; while(slow != null && fast.next != null){ slow = slow.next; fast = fast.next.next; if(slow == fast){ break; } if(slow == null || fast == null){ curHeadA.next = null; return null; } } if(slow == null || fast.next == null){ curHeadA.next = null; return null; } ListNode node1 = headB; ListNode node2 = slow; while(node1 != node2){ node1 = node1.next; node2 = node2.next; } }
public ListNode getIntersectionNode(ListNode headA, ListNode headB){ if(headA == null || headB == null){ return null; } ListNode nodeA = headA; ListNode nodeB = headB; while(nodeA != nodeB){ nodeA = nodeA == null ? headB : nodeA.next; nodeB = nodeB == null ? headA : nodeB.next; } return nodeA; }
部分内容参考:
https://www.cnblogs.com/yorkyang/p/10876604.html