请写一个程序,找到两个单链表最开始的交叉节点。
样例
下列两个链表:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
在节点 c1 开始交叉。
挑战
需满足 O(n) 时间复杂度,且仅用 O(1) 内存。
注意事项
- 如果两个链表没有交叉,返回
null
。 - 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
解题思路1:
时间:O(2 * (LenA + LenB)) = O(LenA + LenB)
空间:O(1)
算法:
- 分别测量两个链表的长度;
- 通过长度比较,令两个链表的头指针都指向 距离末尾节点距离相同 并且 距离最远的地方。其实就是让两个指针指向离交叉点距离相同的位置;
- 一起挪动两个指针,知道他们指向同一个节点;
- 返回任意一个指针指向的节点。
/**
* Definition for ListNode
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
/**
* @param headA: the first list
* @param headB: the second list
* @return: a ListNode
*/
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// write your code here
if(headA == null || headB == null)
return null;
int lenA = measureLength(headA);
int lenB = measureLength(headB);
ListNode nodeA = headA;
ListNode nodeB = headB;
//根据两者长度定比较起始点,使得从起始点开始两者长度一致
while(lenA < lenB){
lenB--;
nodeB = nodeB.next;
}
while(lenA > lenB){
lenA--;
nodeA = nodeA.next;
}
//按顺序比较节点
while(nodeA != nodeB){
nodeA = nodeA.next;
nodeB = nodeB.next;
}
return nodeA;
}
private int measureLength(ListNode head){
int len = 0;
ListNode node = head;
while(node != null){
len++;
node = node.next;
}
return len;
}
}
解题思路2:
解决这个问题的关键是,通过某些方式,让 p1
和 p2
能够同时到达相交节点 c1
。
所以,我们可以让 p1
遍历完链表 A
之后开始遍历链表 B
,让 p2
遍历完链表 B
之后开始遍历链表 A
,这样相当于「逻辑上」两条链表接在了一起。
如果这样进行拼接,就可以让 p1
和 p2
同时进入公共部分,也就是同时到达相交节点 c1
那你可能会问,如果说两个链表没有相交点,是否能够正确的返回 null 呢?
这个逻辑可以覆盖这种情况的,相当于 c1
节点是 null 空指针嘛,可以正确返回 null。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA, p2 = headB;
while(p1 != p2){
if(p1 != null){
p1 = p1.next;
}else{
p1 = headB;
}
if(p2 != null){
p2 = p2.next;
}else{
p2 = headA;
}
}
return p1;
}
}