ฮีป - ให้อัลกอริทึมเวลา


15

อาจเป็นไปได้ว่าคำถามนี้ถูกถามมาก่อน มันมาจาก CLRS (2nd Ed) ปัญหา 6.5-8 -

ให้อัลกอริทึมเวลาเพื่อรวมรายการที่เรียงลำดับkไว้ในรายการที่เรียงลำดับหนึ่งรายการโดยที่nคือจำนวนองค์ประกอบทั้งหมดในรายการอินพุตทั้งหมด (คำแนะนำ: ใช้ min-heap สำหรับการรวมk -way)O(nlgk)knk

เนื่องจากมีรายการที่เรียงลำดับและค่าnทั้งหมดให้เราสมมติว่าแต่ละรายการมีnknหมายเลข kยิ่งไปกว่านั้นแต่ละรายการจะเรียงตามลำดับจากน้อยไปหามากและผลลัพธ์จะถูกเก็บไว้ในลำดับจากน้อยไปหามาก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 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และinsertมีgetMin( ) และ(O(1)incrementIndex ) ซึ่งจะลดปัจจัยคงที่ แต่ไม่ซับซ้อนO(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

สมมติว่าคุณมีรายการเหล่านี้เพื่อรวมรายการ [1] = [1, 10, 15], รายการ [2] = [4, 5, 6] และรายการ [3] = [7, 8, 9] ในการวนซ้ำครั้งแรกค่าจากฮีปจะเป็น 1 และอัลกอริทึมของคุณจะแทรก 10 ลงในฮีป แต่ 10 คือค่าที่ใหญ่ที่สุดของรายการทั้งหมด - คุณจะหลีกเลี่ยงได้อย่างไร
ramgorur

@ramgorur ไม่สำคัญว่า 10 อยู่ในกอง 4,5,6,7,8 และ 9 ทั้งหมดจะได้รับการประมวลผลก่อนเพราะเรามักจะได้รับค่าที่น้อยที่สุดจากฮีปและแทนที่ค่าที่ถูกลบด้วยรายการถัดไปจากรายการเดียวกัน แก้ไขคำตอบพร้อมตัวอย่าง
Dukeling

ดีถ้าเป็นกรณีนี้เราไม่จำเป็นต้องจำรายการเดียวกันสำหรับการผลักองค์ประกอบถัดไป เราสามารถเลือกรายการสุ่มทุกครั้งและผลักองค์ประกอบถัดไปเข้ากอง - ซึ่งควรจะให้ผลลัพธ์เดียวกันฉันก็ใช่ไหม? หรือมีเหตุผลพิเศษอื่น ๆ ที่จะตามอาร์กิวเมนต์รายการเดียวกันหรือไม่
ramgorur

เมื่อลบ4หากคุณเลือกรายการแบบสุ่มคุณอาจท้ายแทรก8ดังนั้นฮีปจะเป็นแบบ[7, 8, 10]ที่คุณจะแทรก7แทน5ลงในชุดผลลัพธ์ซึ่งจะผิด
Dukeling

@ ความคิดเห็น AshwaniGautam ในคำตอบอื่น ๆ เป็น apt: การสร้างกองแรกที่สามารถทำได้ในเวลา ) O(k)
Raphael

13

ก่อนอื่นฉันคิดว่าการสันนิษฐานของคุณในรายการทั้งหมดมีn/k

สำหรับปัญหาของคุณอัลกอริทึมต่อไปนี้ควรทำเคล็ดลับ:

  1. ใส่องค์ประกอบแรกของรายการในนาทีกองขนาดk โปรดจำไว้สำหรับแต่ละองค์ประกอบรายการลิตรเมตรมันเป็น ( O ( k lg)HklmO(klgk)
  2. สำหรับตั้งแต่1ถึง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]


อันที่จริงความซับซ้อนเวลาที่เข้มงวดมากขึ้นจะเป็นO (K + 2 * NlogK) = O (NlogK) O (K) แน่นกว่า O (KlogK)เมื่อทำการฮีป อ้างถึงสิ่งนี้เพื่อความกระจ่างเพิ่มเติม
Ashwani Gautam

O(k)O(klogk)k
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.