目录
https://leetcode.com/problems/intersection-of-two-linked-lists/
找到两个单链表(链表不存在环)交叉的第一个节点,要求时间O(n),空间O(1),同时不能改动链表的原有结构。
一、问题描述
测试用例:
Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
Output: Reference of the node with value = 8
Input Explanation: The intersected node's value is 8 (note that this must not be 0 if the two lists intersect). From the head of A, it reads as [4,1,8,4,5]. From the head of B, it reads as [5,0,1,8,4,5]. There are 2 nodes before the intersected node in A; There are 3 nodes before the intersected node in B.
Input: intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
Output: Reference of the node with value = 2
Input Explanation: The intersected node's value is 2 (note that this must not be 0 if the two lists intersect). From the head of A, it reads as [0,9,1,2,4]. From the head of B, it reads as [3,2,4]. There are 3 nodes before the intersected node in A; There are 1 node before the intersected node in B.
Input: intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
Output: null
Input Explanation: From the head of A, it reads as [2,6,4]. From the head of B, it reads as [1,5]. Since the two lists do not intersect, intersectVal must be 0, while skipA and skipB can be arbitrary values.
Explanation: The two lists do not intersect, so return null.
如果两个单链表没有交叉,返回null。两个单链表交叉有个隐含特性:由于单链表next指针只有一个,两个单链表的重叠部分肯定是在它们的后面部分,而不可能在前面部分。
二、代码实现
1、常规解法
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
//计算A的长度
ListNode cur = headA;
int sizeA = 0;
while (cur != null) {
sizeA++;
cur = cur.next;
}
//计算B的长度
cur = headB;
int sizeB = 0;
while (cur != null) {
sizeB++;
cur = cur.next;
}
//针对一些特殊测试用例
if (sizeA == 1 && sizeB == 1) {
return headA == headB ? headA : null;
}
System.out.println(headA.val+" "+headB.val);
if (sizeA > sizeB) {
ListNode temp = headA;
headA = headB;
headB = temp;
}
System.out.println(headA.val+" "+headB.val);
//now headB is longer
int count = Math.abs(sizeA - sizeB);
//while (headB != null) {
//error
while (headB != null && count>0) {
count--;
headB = headB.next;
}
//now headA's length == headB's length
while (headB != null) {
//if (headB.val == headA.val) {
//input:8 [4,1,8,4,5] [5,0,1,8,4,5] 2 3
//output: Intersected at '1' expected: Intersected at '8'
if (headB == headA) {
break;
}
headB = headB.next;
headA = headA.next;
}
//the first node or null
return headB;
}
}
更简洁的写法:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode A = headA, B = headB;
int i = 0, j = 0;
while (A.next != null) {
A = A.next;
i++;
}
while (B.next != null) {
B = B.next;
j++;
}
//A == B
if (A == B) {
while (i > j) {
headA = headA.next;
i--;
}
while (j > i) {
headB = headB.next;
j--;
}
if (headA == headB) {
return headA;
}
while (headA.next != null && headB.next != null) {
if (headA.next == headB.next) {
return headA.next;
}
headA = headA.next;
headB = headB.next;
}
}
//A != B
return null;
}
}
2、最佳解法
假设链表A为a*c,链表B为b*c,*为两者第一个共同节点,c为除第一个共同节点外的共同部分。从头遍历链表a*cb*c、b*ca*c,由于a*cb和b*ca长度相同,我们肯定能遍历到第二个*,也就是链表A和链表B的第一个共同节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
//O(n+m)
public ListNode getIntersectionNode3(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode A = headA, B = headB;
//Time Limit Exceeded
// while (A != null && B != null) {
// if (A == B) {
// return A;
// }
// A = A.next;
// B = B.next;
// if (A == null) {
// A = headB;
// }
// if (B == null) {
// B = headA;
// }
// }
// return null;
while (A != null && B != null && A != B) {
A = A.next;
B = B.next;
//两种退出条件:A == B找到第一个共同节点、null == null遍历完两遍之后还找不到(O(n+m))
if (A == B) { //没有这一段会Time Limit Exceeded
return A;
}
if (A == null) {
A = headB;
}
if (B == null) {
B = headA;
}
}
return A;
}
}
参考: