大家好,我是Ryan,本篇记录我在学习链表时遇到的一个情况:有环链表。
考察有环链表通常是首先让你判断链表是否有环,若有环,则进一步地让你找到环的入口位置。接下来我们分析这两个问题。
判断链表是否有环
集合
若是学过Java的集合,可以想到用hashset来解决,一边遍历链表,一边加入到集中,同时判断节点是否已经存在。
public ListNode detectCycle(ListNode head) {
ListNode pos =head;
Set<ListNode> visited = new HashSet<ListNode>();
while (pos != null){
if (visited.contains(pos)) {
return pos;
} else {
visited.add(pos);
}
pos = pos.next;
}
return null;
}
这种方法也是我们写工程时常用的,但是面试的时候用这个就体现不出来算法能力了。所以我们还要想另外一个办法。
双指针
这里我们可以用快慢指针来做,首先要想明白这两个指针一定会相遇。
举个例子,两个人在操场跑步,一个人跑得快,一个人跑得慢,这样只要时间足够,快的人一定能再次遇到跑的慢的人。
下面直接给出代码:
public static boolean hasCycleByTwoPoint(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
return true;
}
return false;
}
确定入口
判断出有环之后,我们接着研究环的入口在哪。
这里可以用数学来简单证明:
这里假设快指针在第二圈就遇到了慢指针
图中Z是相遇的点,Y是入口。
可以知道fast指针走了a+b+c+b步,slow指针走了a+b步
那么:
2*(a+b)=a+b+c+b 所以a=c
public static ListNode detectCycleByTwoPoint(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (fast == slow) {
ListNode ptr = head;
while (ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
好了,这次的分享就到这里,下期再见。