一、题目描述
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
二、解题思路
解题思路一
官方答案(作参考):判断两个链表是否相交,可以使用哈希集合存储链表节点。
首先遍历链表headA
,并将链表headA
中的每个节点加入哈希集合中。然后遍历链表headB
,对于遍历到的每个节点,判断该节点是否在哈希集合中:
如果当前节点不在哈希集合中,则继续遍历下一个节点;如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,因此在链表 headB
中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。
如果链表headB
中的所有节点都不在哈希集合中,则两个链表不相交,返回null。
其实按照这个思路,开始我是有一个不理解的部分的,就是如果按照官方大大的想法,那么对于示例一,headB
中第一个出现在集合中的节点应该是1才对,最后我理解了是因为在测试用例中,指明了每个链表在公共节点之前是有几个不公共的节点。
我根据链表改了一下公共节点为1,和到达公共节点前各链表不公共的节点,测试用例也是通过的:
代码演示一
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//存放链表A的所有节点
HashSet<ListNode> visited = new HashSet<ListNode>();
ListNode temp = headA;
//遍历链表A,将所有节点放到集合中
while(temp != null){
visited.add(temp);
temp = temp.next;
}
temp = headB;
while(temp != null){
if(visited.contains(temp)){
return temp;
}
temp = temp.next;
}
return null;
}
}
时间复杂度:O(m+n)
空间复杂度:O(m)
解题思路二
这是参考大佬的一个很巧妙的方法,
设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 cc ,则有:
头节点 headA 到 node 前,共有 a−c 个节点;
头节点 headB 到 node 前,共有 b−c 个节点;
考虑构建两个节点指针 A , B 分别指向两链表头节点 headA , headB ,做如下操作:
指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:a + (b - c)
指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:b + (a - c)
如下式所示,此时指针 A , B 重合,并有两种情况:a+(b−c)=b+(a−c)
若两链表 有 公共尾部 (即 c > 0) :指针 A , B 同时指向「第一个公共节点」node 。
若两链表 无 公共尾部 (即 c = 0 ) :指针 A , B 同时指向 null。
因此返回 A 即可。
下面以a=5 , b=3 ,c=2 示例的算法执行过程。
第一步:A一步步向null遍历,B也一步步向null遍历,当B走完时,A还没走完:
此时,让B走A走过的路,A继续向前走:
A走到空是,它再去走B走过的路:
最后,你会发现当A和B走过相同的路时,他们就会有一个交点:
代码演示二
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA, B = headB;
while(A!=B){
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}
return A;
}
}