《剑指Offer》Java刷题 NO.55 链表中环的入口结点(链表、哈希、双指针)
传送门:《剑指Offer刷题总目录》
时间:2020-07-08
题目:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路:重复问题:哈希;链表问题:快慢指针
方法一:哈希法
依次把点存入,遇到已经存在的点就是环的入口了,否则到null结点还没有重复结点就说明没有环
缺点:需要额外空间
时间复杂度:O(N),空间复杂度O(N)
方法二:快慢指针:fast,slow
- fast一次走两步,slow一步,那么两个指针第一次相遇时【可以通过判断slow指针在走到null结点之前是否和fast相遇来判断是否有环】,fast走了:sFast=x+2y+z+nR【已经绕了n圈了】,slow走了sSlow=x+y,且满足sFast=2sSlow
n=0时,x+2y+z=2x+2y,所以z=x,如下图
n>0时,x+2y+z+nR=2x+2y,所以x=z+nR - slow不动,fast指向头部,然后slow和fast每次都只走一步,两个指针再次相遇的地方就是环的入口
时间复杂度:O(N),空间复杂度O(1)
Java代码:
import java.util.HashSet;
/**
* @author LiMin
* @Title: EntryNodeOfLoop
* @Description: 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
* @date 2020/7/7 20:27
*/
public class EntryNodeOfLoop {
private class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
/**
* 哈希法,需要利用额外空间
*/
public ListNode entryNodeOfLoopOne(ListNode pHead) {
if (pHead == null) {
return null;
}
HashSet<ListNode> listNodes = new HashSet<>();
while (pHead != null) {
if (listNodes.contains(pHead)) {
return pHead;
}
listNodes.add(pHead);
pHead = pHead.next;
}
return null;
}
/**
* 双指针法
*/
public ListNode entryNodeOfLoopTwo(ListNode pHead) {
if (pHead == null) {
return null;
}
ListNode fast = pHead;
ListNode slow = pHead;
while (fast != null && fast.next != null) {//如果没有环的话肯定是fast先到尾巴
fast = fast.next.next;
slow = slow.next;//一开始fast走得快
if (slow == fast) {//一旦相遇就说明有环
fast = pHead;//fast重新指向头部
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;//跳出上面这个while循环时肯定slow==fast即再次相遇
}
}
return null;
}
}