参考自:《剑指Offer——名企面试官精讲典型编程题》
题目:链表中环的入口结点
若一个链表中包含环,如何找出环的入口结点?
主要思路:
第一步:判断链表是否有环。使用双指针,一个指针(慢指针)一次走一步,另一个指针(快指针)一次走两步。如果快指针追得上慢指针,说明有环,否则链表无环。
第二步:求环的长度L。第一步中的快慢指针相遇的结点一定在环中(因为只有快慢指针在环中绕圈时,才能相遇)。因此,从这个相遇的结点出发,一边移动,一边计数,当再次回到该结点时,便可得出环的长度L。
第三步:找到环的入口。使用双指针,第一个指针先走L步,第二个指针再从头开始移动,两个指针速度都是每次一步。当第二个指针指向环的入口时,第一个指针已经围绕环走了一圈回到入口处,即两个指针相遇的结点即为环的入口。
关键点:化繁为简,分步骤,双指针
时间复杂度:O(n)
public class EntryNodeOfLoop {
public static void main(String[] args) {
ListNode head = testData();
ListNode result = entryNodeOfLoop(head);
if (result == null) {
return;
}
//2
System.out.println(result.val);
}
private static ListNode testData() {
ListNode head = new ListNode(1);
ListNode head2 = new ListNode(2);
ListNode head3 = new ListNode(3);
ListNode head4 = new ListNode(4);
head.next = head2;
head2.next = head3;
head3.next = head4;
//尾节点和节点2构成环
head4.next = head2;
return head;
}
private static ListNode entryNodeOfLoop(ListNode pHead) {
//查找相遇的节点
ListNode meetingNode = findMeetNode(pHead);
if (meetingNode == null) {
return null;
}
//计算环的长度
int loopLength = computeLoopLength(meetingNode);
//前面的快指针
ListNode before = pHead;
//后面的慢指针
ListNode after = pHead;
for (int i = 0; i < loopLength; i++) {
before = before.next;
}
while (before.val != after.val) {
before = before.next;
after = after.next;
if (before.val == after.val) {
break;
}
}
return before;
}
/**
* 查找相遇的节点
*
* @param pHead the p head
* @return list node
*/
private static ListNode findMeetNode(ListNode pHead) {
if (pHead == null) {
return null;
}
ListNode slow = pHead.next;
if (slow == null) {
return null;
}
ListNode fast = slow.next;
while (slow != null && fast != null) {
if (slow.val == fast.val) {
return fast;
}
//慢指针走一步
slow = slow.next;
//快指针走两步
fast = fast.next;
if (fast != null) {
fast = fast.next;
}
}
return null;
}
/**
* 计算环的长度
*
* @param node the node
* @return int
*/
private static int computeLoopLength(ListNode node) {
ListNode runNode = node;
int length = 1;
runNode = runNode.next;
//回到原节点,则求出长度
while (node.val != runNode.val) {
runNode = runNode.next;
length++;
}
return length;
}
}
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}