leetcode
leetcode 2551 ~ 2600
链表相交

链表相交

难度:

标签:

题目描述

Given two (singly) linked lists, determine if the two lists intersect. Return the inter­ secting node. Note that the intersection is defined based on reference, not value. That is, if the kth node of the first linked list is the exact same node (by reference) as the jth node of the second linked list, then they are intersecting.

Example 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.

Example 2:

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.

Example 3:

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.

Notes:

  • If the two linked lists have no intersection at all, return null.
  • The linked lists must retain their original structure after the function returns.
  • You may assume there are no cycles anywhere in the entire linked structure.
  • Your code should preferably run in O(n) time and use only O(1) memory.

代码结果

运行时间: 168 ms, 内存: 29.6 MB


// Approach: Using Java 8 Streams is not directly applicable as it doesn't have direct support for linked lists.
// However, we can use iterators or implement custom Spliterators if we want to work with Streams.
// Here, the problem is best solved with the two-pointer technique.
// Thus, we provide a general method outline using Streams for conceptual understanding.

public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) return null;

        ListNode a = headA;
        ListNode b = headB;

        // Using Stream.iterate to traverse the list is not straightforward,
        // hence we'll follow the same two-pointer approach as above.
        while (a != b) {
            a = (a == null) ? headB : a.next;
            b = (b == null) ? headA : b.next;
        }

        return a;
    }
}

解释

方法:

这个解法利用了两个指针p和q分别遍历两个链表headA和headB。初始时,p指向headA的头节点,q指向headB的头节点。当p和q不相等时,继续遍历。如果p到达链表A的尾部,则将p重新指向链表B的头节点;同理,如果q到达链表B的尾部,则将q重新指向链表A的头节点。这样调整后,p和q继续前进,直到它们相遇或都变为null。如果它们相遇,则返回相遇的节点,这就是两链表的交点。如果p和q都变为null,说明两个链表不相交,返回null。

时间复杂度:

O(m+n)

空间复杂度:

O(1)

代码细节讲解

🦆
在此解法中,为什么在p或q到达链表末端时,将它们指向另一个链表的头节点有助于找到交点?
当p或q到达链表末端时,将它们指向另一个链表的头节点,这样做是为了消除两个链表长度不同带来的影响。如果两个链表相交,那么它们从交点到链表末端的长度是相同的。通过让p和q分别遍历完两个链表,确保它们走过的总长度相同,这样就可以保证如果链表相交,p和q会在交点相遇。如果两个链表不相交,这种方式也会让p和q同时到达各自的末端null,因此可以正确返回null。
🦆
此解法中,两个指针最终相遇的节点真的是交点吗,还是有可能是两个链表的尾部null?
在此解法中,两个指针最终相遇的节点如果不是null,那么一定是交点。这是因为两个指针都走过了相同的距离——即两个链表的总长度,此时如果它们在非null节点相遇,只能是在交点相遇。如果两个链表不相交,两个指针会在链表末端的null处相遇,因此可以正确判断链表是否相交。
🦆
在链表不相交的情况下,此算法如何确保最终返回null而不是错误的节点?
算法中,如果两个链表不相交,p和q将分别遍历完两个链表后指向对方的链表。因为两链表不相交,所以它们最终都会到达各自链表的末端null。由于两个指针的行走路径长度相同,它们会同时到达null,这时循环终止,并返回p(或q,此时p和q都是null),从而确保在不相交的情况下返回null。
🦆
此方法是否考虑了所有可能的边界条件,例如一个或两个链表为空的情况?
此方法已经考虑了包括链表为空的情况。如果任一链表为空(即headA或headB为null),则while循环的初始条件`p != q`不成立,因为p和q都是null。循环不执行,直接返回p(或q),此时为null。因此,这种情况下算法正确地返回null,表示链表不相交。

相关问题