给一个链表,若其中包含环,请找出该链表的环入口节点,否则,输出null。
时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M
链表中有环证明 首先在做题之前我们要证明一个链表是否存在环,这里我们用到的就是——快慢指针法。首先给大家介绍两个快慢指针的结论: 1、设置快慢指针,假如有环,他们最后一定相遇。 2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。 接下来我们分别证明结论1、2。 一、首先让我们证明结论1: 我们首先设置快慢指针分别为fast和slow;fast每次走两步,low每次走一步,假如有环两者一定相遇。因为low一旦进环,可以看作fast在后面追赶low的过程,每次两者都接近一次,最后一定能追上。 这个过程类似于链表中倒数第K个结点中思路三的做法。因此,我们结论一就已经证明完毕了。 二、结论2的证明: 首先我们画一个带环的示意图,如下图所示:
**证明:**假设x为环前面的路程(黑色路程),a为环入口到相遇点的路程(蓝色路程,假设顺时针走), c为环的长度(蓝色+橙色路程)。当快慢指针相遇的时候:此时慢指针走的路程为:Sslow = x + m * c + a ①; 快指针走的路程为: Sfast = x + n * c + a ② 即:2 Sslow = Sfast ③ 2 * ( x + mc + a ) = (x + n c + a) ④ 根据①②③④式可得:x = (n - 2 * m )c - a= (n - 2 m -1 )c + c - a,我们解释这个等式即:环前面的路程 = 数个环的长度(这里要让他等于0) + c - a;那么有一个问题:什么是c-a呢?其实从图中可以看出:这是相遇点后,环后面部分的路程。(橙色路程)。因此,我们可以让一个指针从起点A开始走,让一个指针从相遇点B开始继续往后走,2个指针速度一样,那么,当从原点的指针走到环入口点的时候(此时刚好走了x),那么没有经过一个环,说明等式相等。从相遇点开始走的那个指针也一定刚好到达环入口点。因此当二者会相遇,且恰好相遇在环的入口点。这个结论证明完毕。 思路一: 这两个结论就意味着有两种解法。首先我们看结论一的解法。即:直接判断链表中是否有环,然后得到环中节点的数目,最后就是在环中找到入口节点,具体我们用java将其实现。
public class Solution{ public ListNode EntryNodeOfLoop(ListNode pHead){ if(pHead == null) return null; ListNode s = pHead, r = pHead; boolean flag = false; while(r != null && r.next != null){ s = s.next; r = r.next.next; if(s == r){ flag = true; break; } } if(!flag){ return null; }else{ int n = 1; r = r.next; while(s != r){ r = r.next; n++; } s = r = pHead; for(int i = 0; i < n; i++){ r = r.next; } while(s != r){ s = s.next; r = r.next; } return s; } } }
代码效果图如图所示:
如果在牛客网中,此代码可以通过,因为在牛客网中已经为我们定义了链表结构,具体定义如图所示:
但是在本地编译器中我们还需要定义链表结构才能正常运行,具体用Java定义链表结构实现如下:
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }
我们要在本地测试代码的正确性还得加上main(),具体用Java实现如下:
public static void main(String[] args) { // TODO Auto-generated method stub ListNode listNode=new ListNode(1); listNode.next=new ListNode(2); listNode.next=new ListNode(3); listNode.next=new ListNode(4); listNode.next=new ListNode(5); System.out.println(EntryNodeOfLoop(listNode)); }
该代码测试的效果如图所示:
思路二: 根据我们已经证明了的结论二,我们可以让fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇,此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内的相遇点),这次两个指针继续走,一次走一步,相遇的地方就是入口节点。具体我们用java和python两种语言将其实现。 1、首先我们用java将其实现:
public class Solution{ public ListNode EntryNodeOfLoop(ListNode pHead){ ListNode fast = pHead, slow = pHead; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(slow == fast) break; } if(fast == null || fast.next == null) return null; fast = pHead; while (fast != slow){ slow = slow.next; fast = fast.next; } return fast; } }
代码效果图如图所示:
该代码测试的效果如图所示:
2、接下来我们用python将其实现:
class Solution: def EntryNodeOfLoop(self, pHead): if not pHead: return None fast = slow = pHead a = [] while fast.next and fast.next.next: fast = fast.next.next slow = slow.next if fast.val == slow.val: initial = pHead while slow != initial: slow = slow.next initial = initial.next return slow return None
代码效果图如图所示:
但是在本地编译器中我们还需要定义链表结构才能正常运行,具体用python定义链表结构实现如下:
class ListNode: def __init__(self, x): self.val = x self.next = None
思路三: 以上的思路一和思路二是最经典的解法,我们必须掌握,除此之外,我们还有其他的解法,可以利用一个辅助HashMap来解决该题,具体用java实现如下:
import java.util.HashMap; public class Solution{ public ListNode EntryNodeOfLoop(ListNode pHead){ HashMap<ListNode, Integer> first = new HashMap<>(); ListNode pListNode = pHead; while(pListNode != null){ if(!first.containsKey(pListNode)) first.put(pListNode, 1); else{ if(pListNode.next == null) return null; return pListNode; } pListNode = pListNode.next; } return null; } }
代码效果图如图所示:
该代码测试的效果如图所示:
思路四: 当然,我们也可以用一种简单、粗暴的做法,不过这种做法是很耗空间的。接下来我们用python实现该思路:
class Solution: def EntryNodeOfLoop(self, pHead): if not pHead: return None cur = pHead a = [] while cur: a.append(cur) cur = cur.next if not cur: return None if cur in a: return cur
代码效果图如图所示:
本题主要考察大家对链表中环的掌握情况以及找到环的入口节点,本文在解题之前首先给大家介绍如何证明在来链表中有环,并且给出了两个结论以及将其证明。在解答本题中,本文给出了四种解题思路,其中包括两个结论就有对应的两种解题思路,并且在第二种解题思路中,我们分别给出了java和python两种解题过程。另外,我们又给出了利用辅助的HashMap来解决该题以及最后一种就是一种暴力的解法,其最大的缺点就是占用大量的空间,并且虽然最后一种牛客网也可以通过,但是这种解法并不是本题考查的本意。其实我们最好掌握思路一和思路二的解法,这才是文章所考察的核心知识。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!
[1] bo.qiu_xbw [2] 白马长枪儒雅将 [3] 冰河入梦
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句