Heap - Berikan

15

Kemungkinan besar, pertanyaan ini diajukan sebelumnya. Ini dari masalah CLRS (2nd Ed) 6.5-8 -

Berikan algoritma waktu untuk menggabungkanO(nlgk)k diurutkan daftar menjadi satu daftar diurutkan, di mana adalah jumlah total elemen dalam semua daftar masukan. (Petunjuk: Gunakan min-heap untuk penggabungan k -way.)nk

Karena ada daftar diurutkan dan total nilai n , mari kita asumsikan setiap daftar berisi nkn angka, apalagi masing-masing daftar diurutkan dalam urutan naik, dan hasilnya juga akan disimpan dalam urutan naik.nk

Kode semu saya terlihat seperti ini -

    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

Kompleksitas keseluruhan saya menjadi . Saya tidak dapat menemukan cara untuk menghindari loop O ( k ) di dalam O ( n )O(k)+O(k)+O(n(k+2lgk))O(nk+nlgk)O(nk)O(k)O(n)lingkaran untuk menemukan elemen minimum berikutnya dari daftar k. Apakah ada jalan lain? Bagaimana cara mendapatkan algoritma ?O(nlgk)

ramgorur
sumber

Jawaban:

13

Tujuan dari heap adalah untuk memberi Anda minimum, jadi saya tidak yakin apa tujuan dari for-loop ini - for j := 2 to k.

Pandangan saya tentang pseudo-code:

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)

Jadi total kompleksitas waktu adalah O(klogk+n2logk)=O(nlogk)

Anda juga dapat, alih-alih deleteMindan insert, memiliki getMin( ) dan ( O ( log k ) ), yang akan mengurangi faktor konstan, tetapi bukan kompleksitasnya.O(1)incrementIndexO(logk)

Contoh:
(menggunakan nilai daripada indeks dan daftar indeks dan tumpukan diwakili sebagai array yang diurutkan untuk kejelasan)

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
Bangsawan
sumber
katakanlah Anda memiliki daftar ini untuk digabungkan, daftar [1] = [1, 10, 15], daftar [2] = [4, 5, 6] dan daftar [3] = [7, 8, 9]. Pada iterasi pertama, nilai dari heap akan menjadi 1 dan selanjutnya algoritma Anda akan memasukkan 10 ke heap, tetapi 10 adalah nilai terbesar dari semua daftar - bagaimana Anda menghindarinya?
ramgorur
@ramgorur Tidak masalah bahwa 10 ada di tumpukan. 4,5,6,7,8 dan 9 semuanya akan diproses sebelum itu karena kami selalu mendapatkan nilai terkecil dari heap dan terus mengganti nilai yang dihapus dengan item berikutnya dari daftar yang sama. Jawaban yang diedit dengan contoh.
Dukeling
baik, jika ini masalahnya, kita tidak harus benar-benar mengingat daftar yang sama untuk elemen push berikutnya. Kita dapat memilih daftar acak setiap kali dan mendorong elemen berikutnya ke tumpukan - yang juga akan memberikan hasil yang sama, apakah saya benar? Atau adakah alasan khusus lain untuk mengikuti argumen daftar yang sama ?
ramgorur
Saat menghapus 4, jika Anda memilih daftar acak, Anda mungkin berakhir menyisipkan 8, dengan demikian tumpukan itu [7, 8, 10], dari mana Anda akan memasukkan 7alih-alih 5ke dalam set hasil, yang akan salah.
Dukeling
O(k)
13

n/k

Adapun masalah Anda, algoritma berikut harus melakukan trik:

  1. HklmO(klgk)
  2. i1n
    • mHresult[i]O(lgk)
    • mlmHO(lgk)

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

iresultHiresult[1..i]i

resultHresultr1lr1l[1]r1r1l[1]<r1r1result

iriHHmllHresultrimll

result[1..n]

Cornelius Brand
sumber
Sebenarnya kompleksitas waktu yang lebih ketat adalah O (K + 2 * NlogK) = O (NlogK) . O (K) terikat lebih ketat dari O (KlogK), saat membuat Heap. Lihat ini untuk klarifikasi lebih lanjut.
Ashwani Gautam
O(k)O(klogk)k