> 文章列表 > 【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

题目链接:https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/

1. 题目介绍(52. 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version
在节点 c1 开始相交。

【测试用例】:
示例 1:

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version
输入: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。

【条件约束】:

提示

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构.
  • 可假定整个链表结构中没有循环.
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存.

【相关题目】:

注意:本题与主站 160. 相交链表 题目相同.

2. 题解

2.1 枚举 – O(mn)

时间复杂度O(mn),空间复杂度O(1)

解题思路】:
枚举法:在第一个链表上顺序遍历每个节点,每遍历到一个节点,就在第二个链表上顺序比哪里每个节点。如果在第二个链表上有一个节点和第一个链表上的节点一样,则说明两个链表在这个节点上重合,于是就找到了它们的公共接待你。
……
实现策略】:

  1. 先进行无效输入判断;
  2. 创建一个临时节点 tmpB,用来保存链表 B 的头节点;
  3. 双重循环,找出在第二链表上第一个与第一链表相同的节点。
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
class Solution {// Solution1:枚举ListNode getIntersectionNode(ListNode headA, ListNode headB) {// 无效输入判断if (headA == null || headB == null) return null;// 保存 headB 的头节点ListNode tmpB = headB;// 循环判断 A 中的节点是否有和 B 中节点相同的while (headA != null) {// 让 headB 回到头节点上来headB = tmpB;while (headB != null) {if (headA == headB) return headA;// 不同的话,就让 B 移动到下一个节点headB = headB.next;}headA = headA.next;}// 两个链表没有公共节点,返回nullreturn null;}
}

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

2.2 辅助栈 – O(m+n)

时间复杂度O(m+n),空间复杂度O(n)

解题思路】:
如果我们从两个链表的尾部开始往前比较,那么最后一个相同的节点就是我们要找的节点。我们可以通过栈来解决这个问题:分别把两个链表的节点放入两个栈里,这样两个链表的尾节点就位于两个栈的栈顶,接下来比较两个栈顶的节点是否相同。如果相同,则把栈顶弹出接着比较下一个栈顶,直到找到最后一个相同的节点。
……
实现策略】:

  1. 遍历两个链表,并分别将链表存入两个辅助栈中;
  2. 将两个辅助栈分别出栈,找出最后一个共同节点,该节点即为第一个公共节点
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
class Solution {// Solution2:辅助栈ListNode getIntersectionNode(ListNode headA, ListNode headB) {// 无效输入判断if (headA == null || headB == null) return null;// 定义两个辅助栈,用来分别存储链表 A 和 BStack<ListNode> stackA = new Stack<>();Stack<ListNode> stackB = new Stack<>();// 遍历链表A,并将节点全部存入stackAwhile (headA != null) {stackA.push(headA);headA = headA.next;}// 遍历链表B,并将节点全部存入stackBwhile (headB != null) {stackB.push(headB);headB = headB.next;}ListNode pre = null;// 将两个辅助栈分别出栈,找出最后一个共同节点,该节点即为第一个公共节点while (!stackA.isEmpty() && !stackB.isEmpty()) {// 如果 A 和 B 当前的栈顶元素不同,则直接返回栈顶节点if (stackA.peek() != stackB.peek()) break;// 如果相同,则弹出pre = stackA.pop();stackB.pop(); }// 两个链表没有公共节点,返回nullreturn pre;}
}

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

2.3 双指针 – O(m+n)

时间复杂度O(m+n),空间复杂度O(1)
【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

解题思路】:
我们使用两个指针 node1node2 分别指向两个链表 headAheadB 的头结点,然后同时分别逐结点遍历,当 node1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 node2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。
这样,当它们相遇时,所指向的结点就是第一个公共结点。
……
【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version
如上图所示:

  • headA 到首个公共节点的距离为 a-c
  • headB 到首个公共节点的距离为 b-c

……

  • 指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 首个公共节点 时,共走步数为 a+(b−c)
  • 指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 首个公共节点 时,共走步数为:b+(a−c)
  • 此时,我们就可以得到:a+(b−c)=b+(a−c)
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
class Solution {// Solution3:ListNode getIntersectionNode(ListNode headA, ListNode headB) {// 无效输入判断if (headA == null || headB == null) return null;ListNode A = headA, B = headB;while (A != B) {A = A != null ? A.next : headB;B = B != null ? B.next : headA;}// 两个链表没有公共节点,返回nullreturn A;}
}

【LeetCode】剑指 Offer 52. 两个链表的第一个公共节点 p253 -- Java Version

3. 参考资料

[1] 图解 双指针法,浪漫相遇
[2] 剑指 Offer 52. 两个链表的第一个公共节点(双指针,清晰图解)