C语言中的合并链表

C语言中的合并链表,c,linked-list,C,Linked List,这个问题是在一次采访中问我的: 有两个链接列表的两个标题。 在c中有一个合并的链表,其中第二个链表在某个点合并到第一个链表中。 我们如何确定合并点以及找到该点的复杂性是什么 有人能帮忙吗? search = list1->header; if (mixed->header == list1->header) search = list2->header; while (mixed->next != search) mixed = mixed->next;

这个问题是在一次采访中问我的: 有两个链接列表的两个标题。 在c中有一个合并的链表,其中第二个链表在某个点合并到第一个链表中。 我们如何确定合并点以及找到该点的复杂性是什么

有人能帮忙吗?

search = list1->header;
if (mixed->header == list1->header) search = list2->header;

while (mixed->next != search) mixed = mixed->next;
编辑:变量的新名称和一些注释

更新:这假设了两个链表的Y形连接,如Steve Jessop的文章中所述。但我认为,对这个问题的描述非常模糊,因此可以做出各种解释,这只是其中之一

这可以通过单次通过一个列表加上部分通过另一个列表来完成。换句话说,它是开着的

以下是我提出的算法:

创建一个hashmap。是的,如果你手头没有一个库的话,这在C语言中是很忙的。 键将是指向列表1中项目的指针,即头部指针和每个链接。 这些值将是表示位置的整数,即距列表1头部的距离

运行列表1,跟踪位置,并散列所有指针和位置

运行List2,跟踪位置,并找到hashmap中出现的第一个指针

此时,您将知道两个列表共用的第一个节点在列表2中的位置。 hashmap条目还将包含同一节点在列表1中的位置。
这将很好地识别您的合并点。

您的意思是您有一个Y形,如下所示:

清单1:A->B->C->D->E->F

列表2:X->Y->Z->E->F

在哪里。。Z是单链表节点。我们想要找到合并点E,它被定义为两个列表中出现的第一个节点。对吗

如果是这样,那么我会将list2 F的最后一个节点连接到list2 X的第一个节点。这会将list2变成一个循环:

列表2:X->Y->Z->E->F->X->

但更重要的是:

列表1:A->B->C->D->E->F->X->Y->Z->E->

这将问题减少到了,可以及时解决,并增加1个存储空间

但阅读你的问题,另一种可能性是,通过合并,你的意思是插入。您有两个类似的列表:

列表1:A->B->C

列表2:D->E->F

然后是另一个完全独立的列表:

清单3:A->B->D->E->F->C

而这一次,A。。F是列表中包含的值,而不是节点本身

如果这些值都不同,您只需要在列表3中搜索D或D和A中的较晚者,如果您不知道复制到另一个列表中的是哪个列表。这似乎是个毫无意义的问题。如果值可以重复,那么必须检查list3中list2的完整序列。但是,仅仅因为您找到了DEF并不意味着列表2就是在这里插入的——可能DEF在列表1中已经出现过好几次了,而您刚刚找到了其中的第一次。例如,如果我在ABCDEF中插入DEF,结果是ABCDEFDEF,那么我是在索引3还是在索引6中插入的?没有办法说,所以这个问题无法回答


总之,我不明白这个问题。但是,我可能已经回答了它。

< P>如果List1中包含的List2是List1中间的List2点,那么就很容易——只需执行List1并比较指针直到您到达List2. 然而,这样的解释没有多大意义,因为通过像1 2 1一样将list2插入list1,您还可以修改list2-最后1成为list2的一部分

所以我假设问题是关于Y形的:

清单1:A->B->C->D->E->F

列表2:X->Y->Z->E->F

这可以按照Carl的建议使用哈希表来解决

没有哈希表的解决方案是:

遍历列表1并在运行时断开其所有指针 步行表2。当它结束时,您已经到达了连接点 修复列表1中的指针 使用递归可以轻松断开和修复列表1中的指针:

Diconnect(node)
{
    if (node->next == NULL)
      walk list2 to its end, that is the solution, remember it
    else
    {
       tmp = node->next;
       node->next = NULL;
       Disconnect(tmp);
       node->next = tmp;  // repair
    }
}
现在调用Disconnectlist1

这就是向下递归列表1并断开指针。当到达末尾时,执行步骤2 walk list2以查找连接,在从递归返回时修复指针


此解决方案临时修改列表1,因此它不是线程安全的,您应该在Disconnectlist1调用周围使用一个锁。

如果我的答案看起来太简单,很抱歉,但是如果您有两个由标题标识的链表,并且您将它们连接起来,那么

A->B->C->D是第一个列表,并且

1->2->3->4是第二个,那么假设

结果是A->B->C->1->2->3->4->D

然后,要找到合并点,需要遍历最终列表,直到找到第二个标题1。在最坏的情况下,n1是第一个列表的元素数,如果 ond列表在末尾合并

这就是我想问的问题。除非指定,否则对C语言的引用可能意味着没有“对象”或预打包的数据结构


[更新]正如Sebastian所建议的,如果上面两个列表具有相同的元素,我的解决方案将无法工作。我猜想这就是C语言开始发挥作用的地方:您可以搜索第二个列表头的第一个元素的地址。因此,重复的反对意见不会成立。

好吧,有几种方法可以解决这个问题

请注意,我只讨论了从暴力到最佳的方法[角落案例可能需要单独处理]。 考虑N:第一个链表中的节点数 M:第二个链表中的节点数

方法1: 将第一个链表的每个节点与第二个链表的每个其他节点进行比较。找到匹配节点时停止,这是合并点

while(head1)
{
cur2=head2;
while(cur2)
{
    if(cur2==head1)
        return cur2;
    cur2=cur2->next;
}
head1=head1->next;
}
时间复杂度:ON*M 空间复杂度:O1

方法2: 保持两个堆栈。将第一个链表的所有节点推送到第一个堆栈。对第二个链表重复相同的步骤。 开始从两个堆栈中弹出节点,直到两个弹出的节点不匹配。最后一个匹配节点是合并点。 时间复杂度:ON+M 空间复杂性:ON+M

方法3: 利用哈希表。将第一个链表的所有节点插入哈希。 在散列中搜索第二个列表的第一个匹配节点。 这是合并点。 时间复杂度:ON+M 空间复杂性:论 请注意,空间复杂度可能会因所使用的哈希函数而有所不同[在谈论C时,您应该实现自己的哈希函数]

方法4: 将第一个链表的所有节点[按节点,我指的是地址]插入到一个数组中。 在logN时间使用某种稳定的排序算法对数组进行排序[合并排序会更好]。 现在从第二个链表中搜索第一个匹配节点。 时间复杂性:基于logN 空间复杂性:论 注意,这种方法可能比方法3(在空间方面)更好,因为它不使用散列

方法5: 1.取一个大小为M+N的数组。 2.插入第一个链表中的每个节点,然后插入第二个链表中的每个节点。 3.搜索第一个重复元素[可在OM+N时间内的一次扫描中找到]。 时间复杂度:ON+M 空间复杂性:ON+M

方法6:[更好的方法] 1.修改第一个链表并使其循环。 2.现在从第二个链表的开头开始,使用Floyd-Warshall循环检测算法找到循环的开头。 3.删除循环[可以很容易地删除,因为我们知道最后一个节点]。 时间复杂度:ON+M 空间复杂度:O1

方法7:[可能是最好的] 1.计算第一个链表中的节点数[例如c1]。 2.计算第二个链表中的节点数[例如c2]。 3.找出差异[假设c1>c2]diff=c1-c2。 4.取两个指针p1和p2,p1指向第一个链表的头,p2指向第二个链表的头。 5.移动p1差异时间。 6.一次移动每个节点的p1和p2,直到两者都指向同一节点。 7.p1或p2表示合并点。 时间复杂度:ON+M
空间复杂度:O1

平凡解明显在+M.Hm上。。还有更好的。您可以从列表的开始到结束,反之亦然。当您有一个线程时,您可以在某个时间执行这些方向,因此应该会快一点。

//尝试此代码进行合并

   void mergeNode(){  
          node *copy,*current,*current1;

          free(copy);
      merge=NULL;
     current=head;
   current1=head1;
    while(current!=NULL){
    if(merge==NULL){
        node *tmp;
        tmp=(node*)malloc(sizeof(node));
        tmp->data=current->data;
        tmp->link=NULL;
        merge=tmp;
    }
    else{
        copy=merge;
        while(copy->link!=NULL)
            copy=copy->link;
        node *tmp;
        tmp=(node*)malloc(sizeof(node));
        tmp->data=current->data;
        tmp->link=copy->link;
        copy->link=tmp;
    }
    current=current->link;
}
while(current1!=NULL){
    copy=merge;
    while(copy->link!=NULL)
        copy=copy->link;
    node *tmp;
    tmp=(node*)malloc(sizeof(node));
    tmp->data=current1->data;
    tmp->link=copy->link;
    copy->link=tmp;
    current1=current1->link;
}
display(merge);

}

此算法已被破坏。它假定,如果组合链表中的1个元素等于链表2中的第一个元素,则整个列表必须相等。您也无法查看内存位置,因为具有的链表2的节点1的“previous_node”设置为空,而组合列表可能不会设置为空,因为其中将有链表1的位。上面的伪代码搜索中的所有对象,list1->header,list2->header,mixed->header,mixed都是指针。如果其中任何一个重复出现,你就会有一个循环列表。@pmg:你完全正确。我也在考虑在列表1中搜索列表2的内容,但是比较指针是正确的,指针是错误的。我会解释的。假设List2位于列表一的中间。如果要创建组合_列表,直接指向列表1或列表2的节点,则会损坏这些列表。根据您的算法,如果list2.head==combined_list.elementn,那么list2.tail.next将不再指向null,它将指向list1的下一个元素。这会完全破坏您的列表,因此要创建组合列表,您只能复制内容而不能复制指针。但复杂性是如何计算的?我想我们需要澄清:是一个列表完全包含在另一个列表中,还是两个单独的列表连接在一起,就像Y的手臂一样?@carl。。第二个链表在某个点合并到第一个链表中。这明确说明
第二个链表在中间的某个点插入第一个链表。这还不够吗?你的问题非常重要,我希望我们能从OP那里听到它。唉,他已经三个小时没有消息了。啊,好吧,也许他会晚些时候或明天回来,看看答案。我希望它是Y形,因为它非常整洁,可以转变为循环查找,而且我很聪明地发现了它:-如果第一个列表是A->A->A->A,而第二个列表是A->A->A,那就不行了B@sebastian:您可以很容易地使用元素内存中的地址。因此,不会有A->A->A等的情况。我知道,但你的回答没有这么说。@sebastian:很公平。答案已相应更新。
   void mergeNode(){  
          node *copy,*current,*current1;

          free(copy);
      merge=NULL;
     current=head;
   current1=head1;
    while(current!=NULL){
    if(merge==NULL){
        node *tmp;
        tmp=(node*)malloc(sizeof(node));
        tmp->data=current->data;
        tmp->link=NULL;
        merge=tmp;
    }
    else{
        copy=merge;
        while(copy->link!=NULL)
            copy=copy->link;
        node *tmp;
        tmp=(node*)malloc(sizeof(node));
        tmp->data=current->data;
        tmp->link=copy->link;
        copy->link=tmp;
    }
    current=current->link;
}
while(current1!=NULL){
    copy=merge;
    while(copy->link!=NULL)
        copy=copy->link;
    node *tmp;
    tmp=(node*)malloc(sizeof(node));
    tmp->data=current1->data;
    tmp->link=copy->link;
    copy->link=tmp;
    current1=current1->link;
}
display(merge);