检查两个链表是否相交,如果相交,找出交点的位置。

114

这个问题可能有些老,但我想不出答案。

假设有两个不同长度的列表,在某一点 合并;我们如何知道它们的合并点在哪里?

条件:

  1. 我们不知道长度
  2. 我们只能遍历每个列表一次。

两个合并链表的示例。


1
我非常确定,如果不修改列表,它是无法正常工作的。(或者将其复制到其他地方以避免仅解析一次的限制。) - Georg Schölly
2
可能就是这个意思。该死的面试官!呵呵 - Kyle Rosendo
1
我有一个有趣的提议...假设列表的公共尾部是无限长的。如何使用恒定的内存找到节点交集? - Akusete
在不修改列表、使用计数器或进行超过O(N)次迭代的情况下,下面发布了一个解决方案。 - Pavel Radzivilovsky
一个使用golang编写的时间复杂度为O(n^2)和O(n)的示例,后者使用了一个集合: https://play.golang.org/p/XpK3D_p-oq - jpgerek
显示剩余6条评论
27个回答

0

使用JavaScript的解决方案

var getIntersectionNode = function(headA, headB) {
    
    if(headA == null || headB == null) return null;
    
    let countA = listCount(headA);
    let countB = listCount(headB);
    
    let diff = 0;
    if(countA > countB) {

        diff = countA - countB;
        for(let i = 0; i < diff; i++) {
            headA = headA.next;
        }
    } else if(countA < countB) {
        diff = countB - countA;
        for(let i = 0; i < diff; i++) {
            headB = headB.next;
        }
    }

    return getIntersectValue(headA, headB);
};

function listCount(head) {
    let count = 0;
    while(head) {
        count++;
        head = head.next;
    }
    return count;
}

function getIntersectValue(headA, headB) {
    while(headA && headB) {
        if(headA === headB) {
            return headA;
        }
        headA = headA.next;
        headB = headB.next;
    }
    return null;
}

0

Java中的步骤:

  1. 创建一个映射表。
  2. 开始遍历列表的两个分支,并使用与节点相关的某些唯一内容(例如节点ID)作为键将列表的所有遍历节点放入Map中,并在开始时将所有值设置为1。
  3. 每当出现第一个重复键时,增加该键的值(假设现在其值变为2,大于1)。
  4. 获取值大于1的键,这应该是两个列表合并的节点。

1
如果合并部分中存在循环怎么办? - Rohit
除了错误处理循环外,这看起来非常像isyi的答案 - greybeard

0

步骤1:找到两个列表的长度 步骤2:找到差异并将具有差异的最大列表移动 步骤3:现在两个列表将处于类似的位置。 步骤4:迭代列表以找到合并点

//Psuedocode
def findmergepoint(list1, list2):
lendiff = list1.length() > list2.length() : list1.length() - list2.length() ? list2.lenght()-list1.lenght()
biggerlist = list1.length() > list2.length() : list1 ? list2  # list with biggest length
smallerlist = list1.length() < list2.length() : list2 ? list1 # list with smallest length


# move the biggest length to the diff position to level both the list at the same position
for i in range(0,lendiff-1):
    biggerlist = biggerlist.next
#Looped only once.  
while ( biggerlist is not None and smallerlist is not None ):
    if biggerlist == smallerlist :
        return biggerlist #point of intersection


return None // No intersection found

我更喜欢每个项目都以一行开头的列表。考虑使用拼写检查器。 - greybeard

0

您可以将list1的节点添加到哈希集中,然后循环遍历第二个列表,如果list2的任何节点已经存在于集合中,则说明这是合并节点。

static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {
    HashSet<SinglyLinkedListNode> set=new HashSet<SinglyLinkedListNode>();
    while(head1!=null)
    {
        set.add(head1);
        head1=head1.next;
    }
    while(head2!=null){
        if(set.contains(head2){
            return head2.data;
        }
    }
    return -1;
}

0
int FindMergeNode(Node *headA, Node *headB)
{
    Node *tempB=new Node;
    tempB=headB;
   while(headA->next!=NULL)
       {
       while(tempB->next!=NULL)
           {
           if(tempB==headA)
               return tempB->data;
           tempB=tempB->next;
       }
       headA=headA->next;
       tempB=headB;
   }
    return headA->data;
}

你需要在你的回答中添加一些解释。仅有代码的回答可能会被删除。 - rghome
这只是一种暴力方法,将第一个列表的每个元素与第二个列表的每个元素进行比较。这会导致时间复杂度为O(n*m)。 - Amit Dube

-1
按照简单的逻辑来解决这个问题: 由于指针A和B都以相同的速度移动。为了在同一点相遇,它们必须覆盖相同的距离。我们可以通过将一个列表的长度加到另一个列表上来实现这一点。

-1
如果允许编辑链接列表,
1. 只需将列表2的所有节点的下一个节点指针设置为null。 2. 查找列表1中最后一个节点的数据值。这将在单次遍历两个列表时给出相交的节点,而不需要“高级逻辑”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接