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所示)

我想知道这个实现有多复杂:

  • 如果其中一个列表为空,请返回另一个
  • 否则,使用sortedInsert功能将第二个列表的每个节点插入第一个列表,该功能基本上扫描列表,直到找到正确的位置。由于这两个列表都已排序,因此无需每次将节点与第一个列表中的所有节点进行比较,我可以从最后添加的节点开始比较
  • 例:如果已经添加了4,则继续前面的示例,我可以安全地从4开始下一次比较:
    列表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;