힙 정렬 된 목록을 하나의 정렬 된 목록으로 병합 하는 시간 알고리즘 제공


15

아마도이 질문은 전에 요청되었습니다. CLRS (2nd Ed) 문제 6.5-8에서 온 것입니다.

정렬 된 목록을 하나의 정렬 된 목록으로 병합 하는 시간 알고리즘을 제공 하십시오. 여기서 은 모든 입력 목록의 총 요소 수입니다. (힌트 : 병합 에는 최소 힙을 사용하십시오 .)O(nlgk)knk

있기 때문에 목록과 전체 분류 값을, 우리는 각 목록에 포함 된 가정하자 또한 목록의 각 엄격하게 오름차순으로 정렬됩니다 번호를, 그 결과를도 상승에 저장됩니다 주문.knnk

내 의사 코드는 다음과 같습니다.

    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

내 전반적인 복잡성은 됩니다. k 목록에서 다음 최소 요소를 찾기 위해 루프 내에서 루프 를 피하는 방법을 찾을 수 없습니다 . 다른 방법이 있습니까? 알고리즘 을 얻는 방법 ?O(k)+O(k)+O(n(k+2lgk))O(nk+nlgk)O(nk)O(k)O(n)O(nlgk)

답변:


13

힙의 목적은 최소한을 제공하는 것이므로이 for-loop의 목적이 무엇인지 잘 모르겠습니다 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 ( 로그 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 2016 년

@ramgorur 10이 힙에 있다는 것은 중요하지 않습니다. 4,5,6,7,8 및 9는 항상 힙에서 가장 작은 값을 가져오고 삭제 된 값을 동일한 목록의 다음 항목으로 대체하기 때문에 처리되기 전에 처리됩니다. 예를 들어 수정 된 답변입니다.
Dukeling 2016 년

글쎄, 이것이 사실이라면, 우리는 다음 요소 푸시를 위해 실제로 동일한 목록 을 기억할 필요가 없습니다 . 우리는 매번 임의의 목록을 고르고 다음 요소를 힙에 넣을 수 있습니다. 아마도 같은 결과를 낳을 것입니다. 아니면 동일한 목록 인수 를 따르는 다른 특별한 이유가 있습니까?
ramgorur 2016 년

삭제 할 때 4임의의 목록을 선택하면 삽입이 끝날 수 있습니다 8. 따라서 결과 집합 대신 [7, 8, 10]삽입되는 힙이 잘못되어 잘못됩니다. 75
Dukeling 2016 년

다른 답변에 대한 @AshwaniGautam의 의견은 적절합니다. 처음에 힙을 만드는 것은 시간 수행 할 수 있습니다 . O(k)
라파엘

13

우선 , 알고리즘의 실행 시간이 가장 긴 목록 의 길이에 의존하면 항목이있는 모든 목록에 대한 가정이 유효하지 않다고 생각합니다 .n/k

문제에 관해서는 다음 알고리즘이 트릭을 수행해야합니다.

  1. 목록의 첫 번째 요소를 k 의 최소 ​​힙 에 넣습니다 . 각 요소에 대해 목록을 기억 리터 m 이 속한합니다. ( O ( K LG K ) )HklmO(klgk)
  2. 대한 에서 1n은 수행 i1n
    • 최소 추출물 에서 H 와 그것을 저장 (R)의 전자 U의 t [ I ] ( O ( LG K ) )mHresult[i]O(lgk)
    • 직접 ml m (있는 경우)에 H ( O ( lg k ) )에 삽입하십시오mlmHO(lgk)

실행 시간은 로 명확하게 표시되며 알고리즘은 r e s u l t를 올바르게 정렬합니다 .O(klgk+nlgk)=O(nlgk)result

증거 (또는 최소한 증거에 대한 아이디어). 다음 루프 불변을 고려하십시오. r e s u l t 에 삽입 할 번째 요소 는 항상 i 단계에서 최소 힙 H 의 최소값 이므로 r e s u l t [ 1 .. i ] 가 올 바릅니다. i 번째 반복 후에 정렬 됩니다.iresultHiresult[1..i]i

이 첫번째 반복 전에 사실이다 : 첫째, 우리는 첫 번째 요소로 삽입하는 것을 보여준다 에있는 H : 첫 번째 요소에 삽입하는 모순으로 가정 R의 예를 U의 t 하다 ( 전체적으로 가장 작은 요소 인 r 1 )은 첫 번째 요소가 아닙니다. 그리고,리스트에서 L 포함 R 1은 , 제 1 엘리먼트 L [ 1 ] 달라야 R (1) (가정에 의해로서, R 1 이다 하지resultHresultr1lr1l[1]r1r1첫 번째 요소). 목록이 모두 정렬되어 있으므로 도 짝을 이룹니다 . 그러나 r 1전체적으로 가장 작은 요소 로 선택했기 때문에 이는 모순 입니다. 물론, 모든 첫 번째 원소의 최소 삽입하는 하나 의 R의 예를 U의 t는 .l[1]<r1r1result

불변은 반복 후에 유지됩니다. 우리는 같은 방식으로 진행합니다. 삽입 할 번째 요소 ( r i라고 함 )가 H 에 없다고 가정합니다 . 구성함으로써, H는 각리스트로부터 최대 하나의 소자에 유지하고,이 소자가 포함되면 m 리스트에서 에 전임자 모든 L은 이미 추출 된 H 올바르게 삽입 (가설) 및 R의 예를 U의 t . 따라서, r i 는 리스트 l 에서 일부 원소 m 의 후속 인 것으로 가정된다iriHHmllHresultriml. 그러나 이것은 위와 같이 이 정렬 됨에 따라 모순 이므로 불변 값이 유지됩니다.l

종단에서, 우리는 따라서이 올바르게 정렬.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.