ヒープ


15

おそらく、この質問は以前に聞かれます。CLRS(2nd Ed)problem 6.5-8からのものです-

マージに時間アルゴリズムをK 1つのソートされたリストの中にリストをソートし、N、すべての入力リスト内の要素の総数です。(ヒント:k方向のマージには最小ヒープを使用します。)O(nlgk)knk

あるとしてリストをソートし、合計n個の値、私たちはそれぞれのリストが含まれていると仮定しましょうn個kn番号、また各リストのは、厳密に昇順にソートされ、そして結果はまた、昇順に格納されます。nk

私の擬似コードはこのように見えます-

    list[k]   ; k sorted lists
    heap[k]   ; an auxiliary array to hold the min-heap
    result[n] ; array to store the sorted list
    for i := 1 to k                 ; O(k)
    do
        heap[i] := GET-MIN(list[i]) ; pick the first element 
                                    ; and keeps track of the current index - O(1)
    done
    BUILD-MIN-HEAP(heap) ; build the min-heap - O(k)
    for i := 1 to n
    do
        array[i] := EXTRACT-MIN(heap)   ; store the min - O(logk)
        nextMin := GET-MIN(list[1])     ; get the next element from the list 1 - O(1)
        ; find the minimum value from the top of k lists - O(k)
        for j := 2 to k                 
        do
            if GET-MIN(list[j]) < nextMin
                nextMin := GET-MIN(list[j]) 
        done
        ; insert the next minimum into the heap - O(logk)
        MIN-HEAP-INSERT(heap, nextMin)
    done

私の全体的な複雑さは。私は避けるためにどのような方法を見つけることができませんでした O K 内のループを O n個のO(k)+O(k)+O(n(k+2lgk))O(nk+nlgk)O(nk)O(k)O(n)k個のリストから次の最小要素を見つけるためにループします。他の方法はありますか?アルゴリズムを取得する方法は?O(nlgk

回答:


13

ヒープの目的は最小限のものを提供することなので、このforループの目的が何であるかはわかりません- for j := 2 to k

擬似コードの私の見解:

lists[k][?]      // input lists
c = 0            // index in result
result[n]        // output
heap[k]          // stores index and applicable list and uses list value for comparison
                 // if i is the index and k is the list
                 //   it has functions - insert(i, k) and deleteMin() which returns i,k
                 // the reason we use the index and the list, rather than just the value
                 //   is so that we can get the successor of any value

// populate the initial heap
for i = 1:k                   // runs O(k) times
  heap.insert(0, k)           // O(log k)

// keep doing this - delete the minimum, insert the next value from that list into the heap
while !heap.empty()           // runs O(n) times
  i,k = heap.deleteMin();     // O(log k)
  result[c++] = lists[k][i]
  i++
  if (i < lists[k].length)    // insert only if not end-of-list
    heap.insert(i, k)         // O(log k)

したがって、合計時間の複雑度はO(klogk+n2logk)=O(nlogk)

また、deleteMinおよびの代わりにinsertgetMin)および( O log k )を使用することもできます。これにより、定数は減少しますが、複雑さは減少しません。O(1)incrementIndexO(logk

例:(
わかりやすくするために、ソートされた配列として表されるインデックスおよびリストインデックスとヒープではなく値を使用)

Input: [1, 10, 15], [4, 5, 6], [7, 8, 9]

Initial heap: [1, 4, 7]

Delete 1, insert 10
Result: [1]
Heap: [4, 7, 10]

Delete 4, insert 5
Result: [1, 4]
Heap: [5, 7, 10]

Delete 5, insert 6
Result: [1, 4, 5]
Heap: [6, 7, 10]

Delete 6, insert nothing
Result: [1, 4, 5, 6]
Heap: [7, 10]

Delete 7, insert 8
Result: [1, 4, 5, 6, 7]
Heap: [8, 10]

Delete 8, insert 9
Result: [1, 4, 5, 6, 7, 8]
Heap: [9, 10]

Delete 9, insert nothing
Result: [1, 4, 5, 6, 7, 8, 9]
Heap: [10]

Delete 10, insert 15
Result: [1, 4, 5, 6, 7, 8, 9, 10]
Heap: [15]

Delete 15, insert nothing
Result: [1, 4, 5, 6, 7, 8, 9, 10, 15]
Heap: []

Done

これらのリストをマージするには、list [1] = [1、10、15]、list [2] = [4、5、6]、list [3] = [7、8、9]とします。最初の反復では、ヒープからの値は1になり、次にアルゴリズムはヒープに10を挿入しますが、10はすべてのリストの最大値です。どうすればそれを回避できますか?
-ramgorur

@ramgorur 10がヒープ内にあることは重要ではありません。常にヒープから最小値を取得し、削除された値を同じリストの次のアイテムに置き換え続けるため、4,5,6,7,8および9はすべて処理される前に処理されます。例付きの回答を編集しました。
ダケリング

そうだとすれば、次の要素プッシュのために同じリストを実際に覚える必要はありません。毎回ランダムなリストを選択し、次の要素をヒープにプッシュすることができます。これにより、おそらく同じ結果が得られるでしょう。または、同じリスト引数に従う他の特別な理由はありますか?
-ramgorur

を削除するときに4、ランダムなリストを選択すると8、が挿入される可能性があります。したがって、ヒープはになり[7, 8, 10]、結果セットでは7なく、そこからヒープが挿入5されます。
ダケリング

他の答えに関する@AshwaniGautamのコメントは適切です。最初にヒープを作成することは、時間で行うことができます。O(k)
ラファエル

13

まず、アルゴリズムの実行時間が最長リストの長さに依存する場合、エントリを持つすべてのリストの仮定は無効だと思います。n/k

あなたの問題に関しては、次のアルゴリズムがトリックを行うはずです:

  1. リストの最初の要素をサイズkの最小ヒープに入れます。各要素のリストを覚えておいてくださいリットルメートル、それはに属しています。(O k lg k HklmO(klgk)
  2. から1までのnの操作を行います。 i1n
    • Hから最小を抽出し、それをr e s u l t [ i ]mHresult[i]O(lgk)
    • 直接後継挿入におけるL個のMに(もしあれば)をHO mlmHO(lgk)

O(klgk+nlgk)=O(nlgk)result

iresultHiresult[1..i]i

resultHresultr1lr1l[1]r1r1l[1]<r1r1result

iriHHmllHresultrimll

result[1..n]


実際、より厳しい時間の複雑さはO(K + 2 * NlogK)= O(NlogK)になります。ヒープを作成する場合、O(K)はO(KlogK)よりも厳密にバインドされます。詳細については、これを参照してください。
アシュワニゴータム

O(k)O(klogk)k
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.