> 文章列表 > 【Java数据结构——环形链表】判断链表成环与寻找链表成环的入口节点(经典)

【Java数据结构——环形链表】判断链表成环与寻找链表成环的入口节点(经典)

【Java数据结构——环形链表】判断链表成环与寻找链表成环的入口节点(经典)

判断链表是否成环icon-default.png?t=N2N8https://leetcode.cn/problems/linked-list-cycle/description/

解题核心思路:

   定义快慢指针初始引用指向链表的头节点,快指针每向后走两步,慢指针走一步。如果链表中存在环,则快慢指针一定会在某次移动后相遇。

那么,可能你会问,为什么不能让快指针依次移动三步、四步呢?这样不是更快吗?先来下面这张图一起来理解下解答的具体步骤,或许就能明白为什么了。 

那么,使用更大的步幅呢?不妨看下下面这张图:

 虽然也可以实现成环的判断,但是深入思考一下,这种方法在某些情况下可能会有更大的概率错过快慢指针相遇的机会。而使用快指针步幅为2,慢指针步幅为1的方法,能够最大程度上降低这种“错过”概率,在更高的算法效率上得到结果。

解题代码:

/* Definition for singly-linked list.* class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public boolean hasCycle(ListNode head) {if(head == null || head.next == null) {return false;} ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if(fast == slow) {return true;}}return false;}
}

环形链表 Ⅱ (判断链表是否成环,输出入环节点)icon-default.png?t=N2N8https://leetcode.cn/problems/linked-list-cycle-ii/description/

解题核心思路:

1.使用快慢指针判断是否为环形链表,如果不是,直接返回null;如果是,则保留快慢指针相遇处节点的引用

2.从快慢指针相交处节点的引用开始,从链表的头节点引用开始,两个引用分贝向后移动,最后会于链表的入环节点处相交,返回这个引用即可 

 

下面我们来证明为什么快慢指针相交处的节点到入口节点与从链表的头结点开始到入口处节点之间的节点个数相同:

 

我们设:

  • 链表的头节点到入环节点处的长度为 x
  • 链表中环的长度为 R
  • 快慢指针相交处节点到入环节点处的距离为 m.

在求解之前,我们要清楚,极端情况下快指针在走第二圈的过程中一定会与慢指针在环中相遇。ok,请处理了这一点,我们来看:

  • 在快慢指针相遇时,快指针走过的路程为:x+(n*R)+R-m
    慢指针走过的路程为:x+(R-m)
  • 快指针的速度为慢指针速度的2倍,因此有:
    x + (n*R)+R-m = 2*(x+R-m)
  • 化简得到:x = (n-1)R + m

显然,当n取1时,即快指针在环中走第二圈的情况下与慢指针相遇,有x与m相等,即从链表的头节点开始到入环节点的距离与从快慢指针相交处节点开始到入环处的距离相等。

因此,这也是为什么从快慢指针相交处的节点开始,从链表的头节点开始,依次向后移动引用,当这两个引用相交时,引用指向的就是链表中环的入口节点的原因啦!😄

解题代码:

/* Definition for singly-linked list.* class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public ListNode detectCycle(ListNode head) {if(head == null || head.next == null) {return null;}//找到快慢指针相遇的节点ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if(fast == slow) {break;  //找到相遇节点了,跳出循环}} //如果链表中节点没有成环,就直接返回nullif(fast == null || fast.next == null) {return null;}//成环的交点指向的引用向后移动与从链表起始节点开始向后移动的引用在入口节点处重合,返回这个引用即可ListNode cur = head;while(cur != slow) {cur = cur.next;slow = slow.next;}return cur;}
}