今天是秋招预备队算法篇第四天,这是今天的第二题:链表中环的入口结点
问题:链表中环的入口结点
描述:
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围:n≤10000,1<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度 O(n)
解题方法
1、hash表
此题我们最容易想到的方法就是使用hash表,利用hash表存储遍历过的节点,判断当前遍历的节点是否已存在于hash表中,如果不存在,则将该节点添入hash表,如果遍历出第一个已经存在于hash表中的节点,则该节点就是循环链表的入口节点
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//hash表
Set<ListNode> hashSet = new HashSet<ListNode>();
//遍历链表
while(pHead != null){
//判断hash表中是否存在当前节点
if(hashSet.contains(pHead)){
//存在就返回此节点,并且此节点就是循环链表的入口节点
return pHead;
}
//不存在则添加当前节点,并移动节点
hashSet.add(pHead);
pHead = pHead.next;
}
//不是循环链表
return null;
}
}
时间复杂度:O(n)
空间复杂度:O(n)
2、双指针
设计快慢指针,快指针走两步,慢指针走一步,同时遍历链表
设链表入口节点前的链表节点数为x,循环链表的节点数为b,相遇时慢指针走过的节点数为s,快指针走过的节点数为f
首先,快指针的速度是慢指针的两倍,则可以得出以下结论:
①f=2*s
其次,相遇时,快指针除了走过一遍s个节点,快指针还至少在循环链表中循环1次,设快指针的循环次数为n,则有如下结论:
②f=s+nb
由①②可以得出:
s=nb,f=2nb
故可知,两指针相遇时,慢指针已经走了nb步,已知我们要走到入口节点,需要走x + kb步,而这时s = nb只要再走x即可到达入口,我们把快指针移动到头节点,然后两个指针一步一步往后走,当它们相遇时所处的位置就是入口节点
入口节点的循环嵌套在循环链表的判断内部:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//快慢指针
ListNode slow = pHead;
ListNode fast = pHead;
//判断链表是否为循环链表
while(slow != null && fast != null){
slow =slow.next;
if(fast.next != null){
fast = fast.next.next;
}
//链表为循环链表
if(slow == fast){
//从头节点开始一步一步遍历
ListNode index = pHead;
while(index != slow){
index = index.next;
slow = slow.next;
}
return index;
}
}
return null;
}
}
将循环链表的判断和寻找循环链表的入口节点分离,有利于提高代码重复利用,和上述方法的效率理论上是差不多的,但是更规范。
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//判断链表是否为循环链表
ListNode cur = hasCycle(pHead);
if(cur == null){
return null;
}
//从头节点开始一步一步遍历
ListNode index = pHead;
while(index != cur){
index = index.next;
cur = cur.next;
}
return index;
}
//循环链表判断方法
public ListNode hasCycle(ListNode head){
//快慢指针
ListNode slow = head;
ListNode fast = head;
//判断链表是否为循环链表
while(slow != null && fast != null){
slow =slow.next;
if(fast.next != null){
fast = fast.next.next;
}
//链表为循环链表
if(slow == fast){
//返回相遇点
return slow;
}
}
return null;
}
}
时间复杂度:O(n)
空间复杂度:O(1)