C语言中的合并链表
这个问题是在一次采访中问我的: 有两个链接列表的两个标题。 在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;
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);