面试题 02.07. 链表相交

题目

面试题 02.07. 链表相交

要求

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

思路和答案

这道题目先用暴力破解,直接使用双层 for 循环,如下:

/**
 * 暴力破解,双层 for 循环
 *
 * @param headA
 * @param headB
 * @return
 */
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode virtualA = headA;
    while (virtualA != null) {
        ListNode virtualB = headB;
        while (virtualB != null) {
            if (virtualB == virtualA) {
                return virtualA;
            }
            virtualB = virtualB.next;
        }
        virtualA = virtualA.next;
    }
    return null;
}

想一想有没有比这个更好的解决方案呢,可以考虑使用集合呀,Set 和 List 都可以,遍历一个链表,放入集合,遍历另外一个链表,判断是否包含在内,如下:

/**
 * 单层循环,借助 List 结构
 *
 * @param headA
 * @param headB
 * @return
 */
public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
    ListNode virtualA = headA;
    ListNode virtualB = headB;
    List<ListNode> listA = new ArrayList<>();
    while (virtualA != null) {
        listA.add(virtualA);
        virtualA = virtualA.next;
    }
    while (virtualB != null) {
        if (listA.contains(virtualB)) {
            return virtualB;
        }
        virtualB = virtualB.next;
    }
    return null;
}

上面两个题目前者的时间复杂度是 O(m * n),空间复杂度O(1);后者的时间复杂度为O(m + n),空间复杂度为 O(m),用空间换取时间了,这个解决方案在 leetcode 上执行慢的原因在于listA.contains 消耗时间,contains 底层也是通过 for 循环遍历的,所以这里使用 HashMap 替换 List,可以提高性能,如下:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode virtualA = headA;
    ListNode virtualB = headB;
    HashMap<ListNode, Integer> map = new HashMap<>();
    while (virtualA != null) {
        map.put(virtualA, virtualA.val);
        virtualA = virtualA.next;
    }
    while (virtualB != null) {
        if (map.get(virtualB) != null) {
            return virtualB;
        }
        virtualB = virtualB.next;
    }
    return null;
}

接下来说说双指针,想到用双指针,但是没想到怎么用,就是在处理指针如果遍历到链表的结尾应该怎么处理,看了题解之后发现,还可以在指针遍历到链表结尾之后转去遍历另外一个链表,举个简单的例子,链表 A 为 1→2→3→4→null,链表 B 为 5→6→7→8→3→4→null,这个时候指针 C 遍历列表 A,指针 D 遍历列表 B,当 C 遍历到 A 的尾部之后转去遍历链表 B,D 遍历到 B 的结尾之后转去遍历 A,这样下次 C 和 D 遍历的实际链表分别为:

1→2→3→4→5→6→7→8→3→4→null

5→6→7→8→3→4→1→2→3→4→null

上下对比一下就知道结果了,那代码怎么写呢?如下:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode pointA = headA;
    ListNode pointB = headB;
    boolean ab = true;
    boolean ba = true;
    while (pointA != null && pointB != null) {
        if (pointA == pointB) {
            return pointA;
        }
        pointA = pointA.next;
        pointB = pointB.next;
				// 声明 ab 和 ba 的目的是为了防止死循环
        if (pointA == null && ab) {
            pointA = headB;
            ab = false;
        }
        if (pointB == null && ba) {
            pointB = headA;
            ba = false;
        }
    }
    return null;
}
posted @ 2023-12-16 19:52  庄子游世  阅读(4)  评论(0编辑  收藏  举报