链表中环的问题
使用哈希表进行判断
遍历并添加节点到哈希表,如果存在冲突,则表示成环。
如果不存在冲突,则表示未成环
/**
* 方法1:通过HashSet判断
* 遍历并加入Map,key = val, value = node
* 如果存在冲突则表示有环
*
* @param head
* @return
*/
public static boolean hasCycleByMap(ListNode head) {
if (head == null) {
return false;
}
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (!set.add(head)) {
return true;
} else {
head = head.next;
}
}
return false;
}
使用双指针进行判断
使用快慢指针进行判断,快2慢1,如果快==慢,则表示成环。
需要注意判断快指针的边界条件。
/**
* 方法2 通过双指针实现
* 使用快慢指针,快2慢1,如果相遇则表示成环。
*
* @param head
* @return
*/
public static boolean hasCycleByTwoPoint(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
return true;
}
}
return false;
}
确定入口的方法
依然可以使用hashSet进行判断
遍历链表并添加到Set集合中,如果没出现冲突,表示就不存在环;
如果出现冲突,那么第一个冲突点即为环的入口节点。
/**
* 方法1:通过HashMap或者实现
* 遇到重复的第一个元素,就是入口元素
* @param head
* @return
*/
public static ListNode detectCycleByMap(ListNode head) {
if (head == null || head.next == null) {
return null;
}
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return head;
} else {
set.add(head);
}
head = head.next;
}
return null;
}
使用双指针进行判断
使用快慢指针进行判断,快2慢1,如果快==慢,则表示成环。
成环之后,在成环处和链表头节点各放一个指针,同时移动一个节点,相遇处即为入口节点。
/**
* 方法2 通过双指针实现
* 快慢指针先确定成环,在交集处以及head放两个指针,同时一部前进,交汇处为入口节点。
*
* @param head
* @return
*/
public static ListNode detectCycleByTwoPoint(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
ListNode pNode = null;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
pNode = slow;
break;
}
}
if (pNode != null) {
while (head != null){
head = head.next;
pNode = pNode.next;
if (pNode == head) {
return head;
}
}
}
return null;
}
Tips:个人总结,判断链表的边界条件时,可以这样理解,如果while循环体内使用到 node.next.next,则判断条件中一定得有node.next !=null;
如果用到node.next,则判断条件中一定得有node !=null,否则会出现NPE。