4

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. リストの 1 つが空の場合は、もう一方を返すだけです
  2. それ以外の場合は、正しい位置が見つかるまで基本的にリストをスキャンする sortedInsert 関数を使用して、2 番目のリストの各ノードを最初のリストに挿入します。2 つのリストは両方とも既に並べ替えられているため、ノードを最初のリストのすべてのノードと毎回比較する必要はありません。最後に追加されたノードから比較を開始できます。

例: 4 が既に追加されている場合、前の例を続行すると、4 から次の比較を安全に
開始できます 。 ..

1 つの要素を最初のリストのすべての要素と比較すると、m=#list2 および n=#list1 の O(m*n) になりますが、この「最適化」を考慮すると、複雑さは何ですか?

実装:

// 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;
}
4

1 に答える 1

5

実際、ここにあるマージ アルゴリズムは でありO(m + n)、 ではありませんO(m * n)

最後に挿入されたノードへのポインターがあり、そこから次のノードを挿入する場所を探し始めるので、合計数

current = current->next

の操作sortedInsertは最大m + n - 1(結果の長さから 1 を引いたもの) です。挿入操作 (nextポインターの再リンク) の数はn(2 番目のリストの長さ) です。比較ごとに

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

次の操作は挿入またはのいずれかcurrent = current->nextであるため、比較の数は最大で

m + 2*n - 1

結果のリストが最初のリストの要素で始まり、次に2 番目の要素、次に最初m_0のリスト、 2 番目の要素、...、2番目の要素、最後に最初のリストの要素で始まるとします。および0 の場合もありますが、他のすべての数値は厳密に正です。n_1m_1n_2n_rm_rm_0m_r

ブロックの最初の要素は、ブロックの各要素およびブロックの最初の要素n_1と比較されます。そのブロックの他のすべての要素は、2 番目のリストの前の要素、およびブロックの最初の要素と比較されます [との場合は比較が少なくなります]。m_0m_1m_1r = 1m_1 = 0

これにより、m_0 + 1 + (n_1 - 1)*2 = m_0 + 2*n_1 - 1比較が行われます(または少なくなります)。

後のブロックでは、最初の要素がブロックの最後の要素、ブロックのすべての要素、およびブロックの最初の要素[存在する場合]n_kと比較されます。ブロックの後続の要素はすべて、リスト 2 の前の要素と比較され、[存在する場合] ブロックの最初の要素と比較されます。n_(k-1)m_(k-1)m_km_k

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

比較(またはそれ以下)。すべての比較には 2 番目のリストの少なくとも 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.

初期化することで少し改善できます

    struct ListNode *first = head1;

そして、

    if (!first)
        first = head1;

ループからテストします。

于 2013-05-11T23:03:48.690 に答える