合并两个列表的大O复杂性

时间:2013-05-11 07:51:44

标签: algorithm merge time-complexity singly-linked-list

鉴于已经排序的2个单链表,合并列表。

例:
list1:1 2 3 5 7
list2:0 4 6 7 10
---> 0 1 2 3 4 5 6 7 7 10

尽管解决方案非常简单,并且在使用或不使用递归的情况下有几种不同的问题实现(如此http://www.geeksforgeeks.org/merge-two-sorted-linked-lists/,请参阅方法3),

我想知道这个实现的最大复杂性是什么:

  1. 如果其中一个列表为空,则返回另一个
  2. 否则使用sortedInsert函数将第二个列表的每个节点插入第一个节点,该函数基本上扫描列表直到找到正确的位置。由于2个列表都已经排序,因此无需每次将节点与第一个列表中的所有节点进行比较,我就可以从最后添加的节点开始比较。
  3. 例如:如果已添加4,则继续上一个示例我可以安全地从4开始下一个比较:
    list1:0 1 2 3 4 5 7
    list2:6 7 10
    现在比较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;
    }
    

1 个答案:

答案 0 :(得分:3)

实际上,您在此处使用的合并算法是 O(m + n) ,而不是O(m * n)

由于你有一个指向最后一个插入节点的指针,并开始寻找插入下一个节点的位置,所以

的总数
current = current->next
sortedInsert中的

操作最多为m + n - 1(结果长度减去1)。插入操作的数量(重新链接next指针)是n(第二个列表的长度)。每次比较

current->next->data < newNode->data

下一个操作是插入或current = current->next,因此比较次数最多为

m + 2*n - 1

我们假设结果列表以第一个列表中的m_0个元素开头,然后是第二个列表中的n_1个元素,然后是第一个m_1来自n_2的元素第二个,......,n_r来自第二个,然后最后m_r来自第一个。 m_0m_r可能为0,其他所有数字都是正数。

n_1块的第一个元素与m_0块的每个元素和m_1块的第一个元素进行比较。该块的所有其他元素将与其在第二个列表中的前一个元素进行比较,并与m_1块的第一个元素进行比较[除非r = 1m_1 = 0,在这种情况下,比较较少]

这使m_0 + 1 + (n_1 - 1)*2 = m_0 + 2*n_1 - 1比较(或更少)。

对于后来的n_k块,将第一个元素与n_(k-1)块的最后一个元素,m_(k-1)块的所有元素以及{{的第一个元素进行比较1}}阻止[如果存在]。该块的其他元素都与它们在列表2中的前一个元素进行了比较,并且m_k块的第一个元素[如果存在],使得

m_k

比较(或更少)。由于所有比较都涉及第二个列表中的至少一个元素,因此比较总数最多为

 1 + m_(k-1) + 1 + (n_k - 1)*2 = m_(k-1) + 2*n_k

我们可以通过初始化来稍微改善它

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.

并删除

    struct ListNode *first = head1;

从循环中进行测试。