出现环的链表:
主要两种方法:
1.标记法:遍历链表,没遍历一个节点就标记一下,如果遍历到下一节点已经被标记过了就说明出现环了
代码:这里巧妙用到了HashSet,很好用
public static boolean judgmentRing(ListNode list){
Set<ListNode> set = new HashSet<>();
ListNode head = list;
while (head != null){
if(set.contains(head)){
return true;
}else {
set.add(head);
head=head.next;
}
}
return false;
}
2.快慢指针法:设两个指针(让快指针先走),快指针每次移动两个,慢指针每次移动一个,如果有环的话最终两个指针必然会有一刻重合
代码:
public static boolean judgmentRing(ListNode list){
ListNode fast = list;
ListNode slow = list;
while (fast !=null && fast.next!=null){
fast = fast.next.next;
slow=slow.next;
if(fast == slow){
return true;
}
}
return false;
}
那么问题来了,如果链表有环,怎么返回成环位置的节点呢?
我们可以在以上两段代码的基础上进行修改即可
1.标记法:
public static ListNode judgmentRing(ListNode list){
Set<ListNode> set = new HashSet<>();
ListNode head = list;
while (head != null){
if(set.contains(head)){
return head;
}else {
set.add(head);
head=head.next;
}
}
return null;
}
这个比较简单,快慢指针法有些难度
2.快慢指针法,因为最后两个指针相遇只能说明有环,但是位置并不确定,我们怎么能找到成环的节点呢?
这里用到了一个简单的数学推理,接用一下leetcode官方的图片:
最终两个指针在紫色点处相遇,头节点到成环节点距离为a,成环节点和相遇点将环分成了两半,距离分别为b和c
任意时刻,fast 指针走过的距离都为slow 指针的 22 倍。因此,我们有
a+(n+1)b+nc=2(a+b) ==> a=c+(n-1)(b+c)
我们得到一个等量关系发现(n-1)(b+c)就是n个环圈吗
所以当相遇的时候在用一个指针从头节点开始每次移动一个,同时slow指针每次移动一个,当他们相遇(也就是满足上式的=),新指针就一定走了a的距离也就是在成环的节点处
这样就好做了
public ListNode judgmentRing(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast !=null && fast.next!=null){
fast = fast.next.next;
slow=slow.next;
if(fast == slow){
ListNode ptr = head;
while (ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}