一.题目描述
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
提示:
listA 中节点数目为 m
listB 中节点数目为 n
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]
进阶:你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?
二.题目解析
1.消除长度差
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
/*因为两个链表相交部分往后的节点都相同,所以若长度不相同则多余的长度一定在前面
1->2->3->4->5
1->2->6->3->4->5
时间复杂度O(m+n),空间复杂度O(1)
* */
ListNode p1 = headA,p2 = headB;
int count1 = 0,count2 = 0;
//获取到两个链表各自的长度
while (p1 != null){
count1++;
p1 = p1.next;
}
while (p2 != null){
count2++;
p2 = p2.next;
}
p1 = headA;
p2 = headB;
int diff = count1 - count2;
int abs = Math.abs(diff);
//指向长度较长的那个链表的指针先走多余的步数
if(diff != 0){
while (abs > 0){
p1 = diff < 0 ? p1 : p1.next;
p2 = diff > 0 ? p2 : p2.next;
abs--;
}
}
//两个指针同时开始走
while (p1 != null && p2 != null){
if(p1 == p2){
return p1;
}
p1 = p1.next;
p2 = p2.next;
}
return null;
}
2.消除长度差另一种方式
public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
/*也是利用了两链表长度差,时间复杂度O(m+n)(最差情况下无公共节点,需要遍历m+n个节点),空间复杂度O(1)
当原较长链表的指针指向较短链表的head时,原较短链表的指针指向较长链表的head+长度差的距离,
也就消除了"长度差"的影响,此时两指针一起向前遍历即可
* */
ListNode p1 = headA,p2 = headB;
while (p1 != p2){
p1 = p1 == null ? headB : p1.next;
p2 = p2 == null ? headA : p2.next;
}
//有公共节点则返回这个公共节点,无公共节点则都指向的是null,也是返回p1即可
return p1;
}
3.造环,问题转化为求环入口
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
/*造环,将A链首尾相连,若A、B有交点,即相当于B链有环,转化为求B链环口位置
时间复杂度O(m+n),空间复杂度O(1)
* */
ListNode p1 = headA,p2 = headB;
while (p1.next != null){
p1 = p1.next;
}
//将A链表首尾相连
p1.next = headA;
//快慢指针同时指向headB,寻找环入口
ListNode fast = headB,slow = headB;
while (fast != null && fast.next != null){
//快慢指针按照不同的速度移动
fast = fast.next.next;
slow = slow.next;
//此时在环内相遇
if(fast == slow){
//fast指针指向起点
fast = headB;
//fast指针和slow指针到环口等距离,两指针同时向前走即可同步到达环入口
while (fast != slow){
fast = fast.next;
slow = slow.next;
}
//取消A链表的首尾相连
p1.next = null;
return fast;
}
}
//取消A链表的首尾相连
p1.next = null;
return null;
}