Algorithm 合并两个列表非常复杂
给定已排序的两个单链接列表,合并列表。 示例:Algorithm 合并两个列表非常复杂,algorithm,merge,time-complexity,singly-linked-list,Algorithm,Merge,Time Complexity,Singly Linked List,给定已排序的两个单链接列表,合并列表。 示例: 清单1:12357 清单2:046710 --->0123456710 尽管解决方案非常简单,并且有几个不同的问题实现,不管是否使用递归(如方法3所示) 我想知道这个实现有多复杂: 如果其中一个列表为空,请返回另一个 否则,使用sortedInsert功能将第二个列表的每个节点插入第一个列表,该功能基本上扫描列表,直到找到正确的位置。由于这两个列表都已排序,因此无需每次将节点与第一个列表中的所有节点进行比较,我可以从最后添加的节点开始比较 例:如果
清单1:12357
清单2:046710
--->0123456710 尽管解决方案非常简单,并且有几个不同的问题实现,不管是否使用递归(如方法3所示) 我想知道这个实现有多复杂:
列表1:01123457
清单2:6710
现在将6与4进行比较,而将其与1 2 3 4进行比较 如果我将一个元素与第一个列表中的所有元素进行比较,它将是O(m*n)、m=#list2和n=#list1,但考虑到这种“优化”,复杂性是什么? 实施:
// Insert a new node in a sorted list
int sortedInsert(struct ListNode **head, struct ListNode* newNode) {
int headUpdated = 0;
struct ListNode *current;
// The list is empty or the new element has to be added as first element
if (*head == NULL || (*head)->data >= newNode->data) {
newNode->next = *head;
*head = newNode;
headUpdated = 1;
}
else {
// Locate the node before the point of insertion
current = *head;
while (current->next != NULL && current->next->data < newNode->data)
current = current->next;
newNode->next = current->next;
current->next = newNode;
}
return headUpdated;
}
struct ListNode *mergeLists(struct ListNode *head1, struct ListNode *head2) {
if (head1 == NULL)
return head2;
if (head2 == NULL)
return head1;
// Store the node in the first list where to start the comparisons
struct ListNode *first = head1;
while (head2) {
struct ListNode *head2Next = head2->next;
printf("Adding %d starting comparison from %d\n", head2->data, first->data);
if (sortedInsert(&first, head2))
head1 = first;
first = head2;
head2 = head2Next;
}
return head1;
}
//在排序列表中插入新节点
int sortedInsert(结构列表节点**头,结构列表节点*新节点){
int=0;
结构ListNode*当前;
//列表为空,或者必须添加新元素作为第一个元素
如果(*head==NULL | |(*head)->data>=newNode->data){
新建节点->下一步=*头部;
*头=新节点;
头部更新=1;
}
否则{
//在插入点之前定位节点
电流=*水头;
while(当前->下一步!=NULL&¤t->next->datadata)
当前=当前->下一步;
新建节点->下一步=当前->下一步;
当前->下一步=新节点;
}
更新返回标题;
}
结构ListNode*MergeList(结构ListNode*head1,结构ListNode*head2){
if(head1==NULL)
返回头2;
if(head2==NULL)
返回头1;
//将节点存储在开始比较的第一个列表中
结构ListNode*first=head1;
while(标题2){
结构列表节点*head2Next=head2->next;
printf(“添加从%d开始比较的%d\n”,head2->data,first->data);
if(分拣机(&第一台,机头2))
头1=第一;
第一个=头2;
head2=head2Next;
}
返回头1;
}
实际上,这里的合并算法是O(m+n)
,而不是O(m*n)
由于您有一个指向上一个插入节点的指针,并从该指针开始查找插入下一个节点的位置,因此
current = current->next
sortedInsert中的操作最多为m+n-1
(结果长度减1)。插入操作的数量(重新链接下一个指针)为n
(第二个列表的长度)。每次比较
current->next->data < newNode->data
假设结果列表从第一个列表的m_0
元素开始,然后是第二个列表的n_1
元素,然后是第一个列表的m_1
,第二个列表的n_2
,第二个列表的n_r
,最后是第一个列表的m_r
m_0
和m_r
可能为0,所有其他数字都严格为正
将n_1
块的第一个元素与m_0
块的每个元素以及m_1
块的第一个元素进行比较。将该块的所有其他元素与第二个列表中的前一个元素以及m_1
块的第一个元素进行比较[除非r=1
和m_1=0
,在这种情况下比较较少]
这使得m_0+1+(n_1-1)*2=m_0+2*n_1-1
比较(或更少)
对于后面的n_k
块,将第一个元素与n_k(k-1)
块的最后一个元素、m_k(k-1)
块的所有元素以及m_k
块的第一个元素(如果存在)进行比较。块的其他元素都与列表2中的前一个元素进行比较,m_k
块的第一个元素[如果存在的话],这使得
1 + m_(k-1) + 1 + (n_k - 1)*2 = m_(k-1) + 2*n_k
比较(或更少)。由于所有比较都至少涉及第二个列表中的一个元素,因此比较的总数最多为
m + 2*n - 1
m_0 + 2*n_1 - 1 + m_1 + 2*n_2 + m_2 + 2*n_3 + ... + m_(r-1) + 2*n_r <= m + 2*n - 1.
以及移除
if (!first)
first = head1;
从循环中进行测试。@H2CO3否,每次比较后都会插入或插入current=current->next
。有n
插入,最多有m+n-1
改进。如果你坐下来计算操作顺序,你会发现你的版本相当于标准的列表合并,只是编写方式不同而已。@DanielFischer哦,有两个因素,对吧。我的错。谢谢Daniel对O(m+n)的正式演示。很高兴看到我的实现比我想象的更好!我将很快更新我已接受的答案。
if (!first)
first = head1;