【问题标题】:Finding the intersecting node from two intersecting linked lists从两个相交的链表中找到相交的节点
【发布时间】:2011-01-14 01:43:24
【问题描述】:

假设有两个单链表,它们都在某个点相交并成为单链表。

两个链表的头或开始指针都是已知的,但相交的节点是未知的。此外,每个列表中的节点在它们相交之前的数量是未知的,并且两个列表可能不同,即 List1 在到达交叉点之前可能有 n 个节点,而 List2 在到达交叉点之前可能有 m 个节点,其中 m 和 n 可能是

  • m = n,
  • m
  • m > n

一个已知或简单的解决方案是将第一个列表中的每个节点指针与第二个列表中的每个其他节点指针进行比较,通过匹配的节点指针将我们引导到相交节点。但是,这种情况下的时间复杂度将是 O(n2),这会很高。

找到相交节点的最有效方法是什么?

【问题讨论】:

标签: c algorithm linked-list


【解决方案1】:

这需要 O(M+N) 时间和 O(1) 空间,其中 M 和 N 是链表的总长度。如果公共部分很长(即 M,N >> m,n)

,则可能效率低下
  1. 遍历两个链表找到M和N。
  2. 回到头部,然后遍历 |M - N|较长列表中的节点。
  3. 现在步调一致并比较节点,直到找到共同的节点。

编辑:查看更多here

【讨论】:

  • 接受这个答案,因为这不需要修改列表,也不会占用额外的空间。但是,我仍然想知道是否没有比这更好的解决方案。无论如何,非常感谢您和其他人的回复。
  • @Jakob Borg 的答案不会比计算两个链表的长度差少一轮迭代吗?
  • @user1071840:是的,不同之处在于 Jakob 的答案需要 O(N) 额外空间来存储颜色。
【解决方案2】:

在这一点上可能无关紧要,但这是我肮脏的递归方法。 这需要O(M) 时间和O(M) 空间,其中M >= N 用于list_M,长度为Mlist_N 长度为N

  1. 递归迭代到两个列表的末尾,然后从末尾开始计数步骤2。请注意list_N 将在list_M 之前命中null,对于M > N
  2. list_M != list_N && list_M.next == list_N.next 相交时,M=N 的长度相同
  3. 不同长度M>N 相交时list_N != null

代码示例:

Node yListsHelper(Node n1, Node n2, Node result) {
    if (n1 == null && n2 == null)
        return null;
    yLists(n1 == null ? n1 : n1.next, n2 == null ? n2 : n2.next, result);
    if (n1 != null && n2 != null) {
        if (n2.next == null) { // n1 > n2
            result.next = n1;
        } else if (n1.next == null) { // n1 < n2
            result.next = n2;
        } else if (n1 != n2 && n1.next == n2.next) { // n1 = n2
            result.next = n1.next; // or n2.next
        }
    }
    return result.next;
}

【讨论】:

    【解决方案3】:

    这是我在深夜编码时发现的疯狂解决方案,它比公认的答案慢 2 倍,但使用了一个不错的算术技巧:

    public ListNode findIntersection(ListNode a, ListNode b) {
        if (a == null || b == null)
            return null;
    
        int A = a.count();
        int B = b.count();
    
        ListNode reversedB = b.reverse();
    
        // L = a elements + 1 c element + b elements
        int L = a.count();
    
        // restore b
        reversedB.reverse();
    
        // A = a + c
        // B = b + c
        // L = a + b + 1
    
        int cIndex = ((A+B) - (L-1)) / 2;
        return a.atIndex(A - cIndex);
    }
    

    我们将列表分为三个部分:a 这是第一个列表的一部分,直到公共部分开始,b 这是第二个列表的一部分,直到公共部分,c 这是两个列表的公共部分列表。我们计算列表大小然后反向列表b,这将导致当我们从a 开始遍历列表时,我们将在reversedB 结束(我们将转到a -&gt; firstElementOfC -&gt; reversedB)。这将为我们提供三个等式,使我们能够获得公共部分的长度c

    这对于编程竞赛或在生产中使用来说太慢了,但我认为这种方法很有趣。

    【讨论】:

      【解决方案4】:

      将两个列表的内容(或地址)转储到一个哈希表中。第一次碰撞是你的路口。

      【讨论】:

      • 如果有 2 个链表,如 1-2-3-4-3-5 和 9-8-3-5。并且第二个链表与第二个第三个中的第一个相交。但是如果我们使用一些哈希表,那么它将在 3 的第一个位置发生冲突。
      • 一个链表不能包含两次 3,除非它已经被扭曲成“6”而不是线性链表。
      • 链表可以有重复值,所以重复 3 是非常合法的。地址冲突是正确的方法。
      【解决方案5】:

      检查每个列表的最后一个节点,如果有交集,它们的最后一个节点将相同。

      【讨论】:

      • 本题意在寻找相交节点。
      【解决方案6】:

      如果可能,您可以在节点中添加“颜色”字段或类似字段。遍历其中一个列表,为节点着色。然后遍历第二个列表。一旦你到达一个已经着色的节点,你就找到了交叉点。

      【讨论】:

      • 只有在允许更改列表时才有可能。
      • 不一定。您可以使用外部数组或哈希表来存储颜色。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-12
      相关资源
      最近更新 更多