힙을 어떻게 복잡하게 만들 수 있습니까?


494

누군가 힙을 만드는 것이 어떻게 복잡성을 설명 할 수 있습니까?

항목을 힙 O(log n)에 삽입하면 삽입이 n / 2 번 반복됩니다 (나머지는 나뭇잎이며 힙 속성을 위반할 수 없음). 따라서 이것은 복잡성이되어야한다는 것을 의미합니다 O(n log n).

다시 말해, 우리가 "힙화"하는 각 항목에 대해 지금까지 힙의 각 레벨 (로그 n 레벨)에 대해 한 번 필터링해야 할 가능성이 있습니다.

내가 무엇을 놓치고 있습니까?


힙을 "빌드"한다는 것은 정확히 무엇을 의미합니까?
mfrankli

힙 정렬에서와 같이 정렬되지 않은 배열을 가져 와서 힙 규칙에 맞을 때까지 각각의 상위 절반 요소를
필터링합니다.

2
내가 찾을 수있는 유일한 링크는 다음과 같습니다. Buildheap의 복잡성은 Θ (n lg n) – n 호출 당 Θ (lg n)의 비용으로 Heapify에 대한 호출로 보이지만이 결과는 Θ (n)으로 향상 될 수 있습니다 cs.txstate.edu/~ch04/webtest/teaching/courses/5329/lectures/…
GBa

2
@Gba는 MIT에서이 비디오를 시청합니다. 그는 약간의 수학으로 O (n)을 얻는 방법에 대해 잘 설명합니다. youtube.com/watch?v=B7hVxCmfPtM
CodeShadow

2
@CodeShadow가 설명에 대한 직접 링크 : youtu.be/B7hVxCmfPtM?t=41m21s
sha1

답변:


435

이 주제에 묻힌 몇 가지 질문이 있다고 생각합니다.

  • O (n) 시간에 buildHeap실행되도록 어떻게 구현 합니까?
  • 올바르게 구현했을 때 O (n) 시간에 buildHeap실행되는 것을 어떻게 표시 합니까?
  • 힙 정렬 이 O (n log n)가 아닌 O (n) 시간에 실행되도록 동일한 논리가 작동하지 않는 이유는 무엇 입니까?

O (n) 시간에 buildHeap실행되도록 어떻게 구현 합니까?

종종,이 질문에 대한 대답의 차이에 초점을 siftUp하고 siftDown. 사이의 올바른 선택을 siftUp하고하는 것은 siftDown얻는 것이 중요합니다 O (n)의 성능을 buildHeap하지만, 도움의 일에 아무 차이 이해하지 않습니다 buildHeapheapSort일반적입니다. 사실, 모두의 적절한 구현 buildHeapheapSort것이다 에만 사용 siftDown. siftUp가 예를 들어, 이진 힙을 사용하여 우선 순위 큐를 구현하는 데 사용 될 수 있도록 운영은 기존 힙에 삽입을 수행하기 위해 필요합니다.

최대 힙 작동 방식을 설명하기 위해 이것을 작성했습니다. 이는 일반적으로 힙 정렬 또는 높은 값이 높은 우선 순위를 나타내는 우선 순위 큐에 사용되는 힙 유형입니다. 최소 힙도 유용합니다. 예를 들어, 정수 키가있는 항목을 오름차순으로 검색하거나 문자열을 알파벳 순서로 검색 할 때. 원칙은 동일합니다. 정렬 순서를 전환하기 만하면됩니다.

힙 속성 을 지정 이진 힙의 각 노드는 적어도 그 아이 모두 큰대로해야한다는. 특히 이것은 힙에서 가장 큰 항목이 루트에 있음을 의미합니다. 시프 팅 다운 및 시프 팅은 기본적으로 반대 방향으로 동일한 작업입니다.

  • siftDown 하위 노드가 너무 커질 때까지 하위 노드가 너무 큰 노드를 가장 큰 하위 노드로 바꿉니다 (아래로 이동).
  • siftUp 위의 노드보다 크지 않을 때까지 상위 노드와 너무 큰 노드를 교체 (위로 이동)합니다.

연산의 수는 필요 siftDown하고 siftUp, 노드가 이동할 수있는 거리에 비례한다. 의 경우 siftDown, 트리의 맨 아래까지의 거리이므로 트리 siftDown의 맨 위에있는 노드의 경우 비용이 많이 듭니다. 을 사용 siftUp하면 작업이 트리의 상단까지의 거리에 비례하므로 트리 siftUp의 하단에있는 노드의 경우 비용이 많이 듭니다. 최악의 경우 두 작업이 모두 O (log n) 이지만 힙에서는 하나의 노드 만 맨 위에 있고 반면에 절반은 맨 아래 레이어에 있습니다. 따라서 모든 노드에 작업을 적용해야하는 경우 siftDownover를 선호한다는 것은 놀라운 일이 아닙니다 siftUp.

buildHeap함수는 정렬되지 않은 항목의 배열을 가져 와서 모두 힙 특성을 충족 할 때까지 이동하여 유효한 힙을 생성합니다. 위에서 설명한 및 작업 을 buildHeap사용하기 위해 취할 수있는 두 가지 방법이 있습니다 .siftUpsiftDown

  1. 힙 상단 (어레이의 시작)에서 시작 siftUp하여 각 항목을 호출 합니다. 각 단계에서 이전에 선별 된 항목 (배열에서 현재 항목 이전의 항목)은 유효한 힙을 형성하고 다음 항목을 선별하면 힙에서 유효한 위치에 배치됩니다. 각 노드를 선별 한 후 모든 항목이 힙 특성을 충족시킵니다.

  2. 또는 반대 방향으로 이동하십시오. 어레이의 끝에서 시작하여 앞쪽으로 뒤로 이동하십시오. 반복 할 때마다 올바른 위치에 놓일 때까지 항목을 분류합니다.

어떤 구현 buildHeap이 더 효율적입니까?

이 두 가지 솔루션 모두 유효한 힙을 생성합니다. 당연히 더 효율적인 방법은를 사용하는 두 번째 작업입니다 siftDown.

하자 H = 로그를 N 힙의 높이를 나타냅니다. siftDown접근에 필요한 작업 은 합계로 제공됩니다.

(0 * n/2) + (1 * n/4) + (2 * n/8) + ... + (h * 1).

합계의 각 항에는 주어진 높이에서 노드가 이동해야하는 최대 거리 (하단 레이어의 경우 0, 루트의 경우 h)에 해당 높이의 노드 수를 곱한 값이 있습니다. 반대로 siftUp각 노드 에서 호출하는 합계 는

(h * n/2) + ((h-1) * n/4) + ((h-2)*n/8) + ... + (0 * 1).

두 번째 합계가 더 큽니다. 첫 번째 항만 hn / 2 = 1/2 n log n이므로이 방법은 최상의 O (n log n) 복잡성을 갖습니다 .

siftDown접근법 의 합 이 실제로 O (n) 임을 어떻게 증명할 수 있습니까?

한 가지 방법 (작동하는 다른 분석도 있음)은 유한 합계를 무한 계열로 변환 한 다음 Taylor 계열을 사용하는 것입니다. 첫 번째 용어 인 0을 무시할 수 있습니다.

buildHeap 복잡성을위한 Taylor 시리즈

각 단계가 왜 작동하는지 확실하지 않은 경우 다음과 같이 프로세스에 대한 정당화가 있습니다.

  • 항은 모두 양수이므로 유한 합은 무한 합보다 작아야합니다.
  • 이 계열은 x = 1 / 2로 평가 된 검정력 계열과 같습니다 .
  • 이 검정력 계열은 f (x) = 1 / (1-x) 에 대한 Taylor 계열의 도함수와 같습니다 (상수 ) .
  • x = 1 / 2 는 해당 Taylor 계열의 수렴 간격 내에 있습니다.
  • 따라서 Taylor 계열을 1 / (1-x)로 바꾸고 , 차별화하여 평가하여 무한 계열의 값을 찾을 수 있습니다.

무한 합은 정확히 n 이므로 유한 합은 더 크지 않으므로 O (n) 입니다.

힙 정렬에 O (n log n) 시간이 필요한 이유는 무엇 입니까?

buildHeap선형 시간 으로 실행할 수 있다면 왜 힙 정렬에 O (n log n) 시간이 필요합니까? 힙 정렬은 두 단계로 구성됩니다. 먼저 buildHeap배열 을 호출 하는데, 최적으로 구현 된 경우 O (n) 시간 이 필요합니다 . 다음 단계는 힙에서 가장 큰 항목을 반복적으로 삭제하여 배열의 끝에 배치하는 것입니다. 힙에서 항목을 삭제하기 때문에 힙을 종료 한 직후에는 항목을 저장할 수있는 열린 지점이 항상 있습니다. 따라서 힙 정렬은 다음으로 큰 항목을 연속적으로 제거하고 마지막 위치에서 시작하여 앞쪽으로 이동하여 배열에 배치하여 정렬 된 순서를 달성합니다. 힙 정렬에서 지배하는 것은이 마지막 부분의 복잡성입니다. 루프는 이것을 좋아합니다 :

for (i = n - 1; i > 0; i--) {
    arr[i] = deleteMax();
}

분명히 루프는 O (n) 번 실행됩니다 ( 정확하게하려면 n-1 , 마지막 항목은 이미 제자리에 있음). deleteMax힙 의 복잡도 는 O (log n) 입니다. 일반적으로 루트 (힙에 남아있는 가장 큰 항목)를 제거하고 힙에있는 마지막 항목 (잎) 인 가장 작은 항목 중 하나로 대체하여 구현됩니다. 이 새로운 루트는 힙 속성을 거의 확실히 위반하므로 siftDown다시 허용 가능한 위치로 이동할 때까지 호출해야합니다 . 이것은 또한 다음으로 큰 항목을 루트까지 이동시키는 효과가 있습니다. 트리의 맨 아래에서 buildHeap호출하는 대부분의 노드의 위치 와 달리 siftDown이제는 호출siftDown 각 반복의 트리의 상단에서!나무는 줄어들지 만 충분히 빨리 줄어들지 않습니다 . 나무의 높이는 노드의 전반부를 제거 할 때까지 (하단 레이어를 완전히 지울 때) 일정하게 유지됩니다. 그런 다음 다음 분기의 높이는 h-1 입니다. 이 두 번째 단계의 총 작업은

h*n/2 + (h-1)*n/4 + ... + 0 * 1.

스위치를 확인하십시오. 이제 제로 작업 사례는 단일 노드에 해당 하고 h 작업 사례는 노드의 절반에 해당합니다. 이 합은 siftUp을 사용하여 구현 되는 비효율적 인 버전과 마찬가지로 O (n log n)buildHeap 입니다. 그러나이 경우 정렬을 시도하고 있으므로 다음으로 큰 항목을 제거해야하므로 선택의 여지가 없습니다.

요약하면 힙 정렬 작업은 두 단계의 합입니다. buildHeap의 O (n) 시간과 O (n log n)는 각 노드를 순서대로 제거 하므로 복잡도는 O (n log n) 입니다. 당신은 (정보 이론의 일부 아이디어를 사용하여) 비교 기반 정렬의 경우 O (n log n) 이 어쨌든 기대할 수있는 최선임을 증명할 수 있으므로 이것에 실망하거나 힙 정렬을 기대할 이유가 없습니다. O (N) 시간은 결합 buildHeap한다.


2
대부분의 다른 사람들이 그것을 참조하는 것처럼 보이고 힙 정렬을위한 최선의 선택이므로 max heap을 사용하도록 내 대답을 편집했습니다.
Jeremy West

28
"직접 하나의 노드 만있는 반면에 절반의 노드는 하위 계층에 있습니다. 따라서 모든 노드에 작업을 적용해야한다고해도 놀랄 일이 아닙니다. siftUp보다 siftDown을 선호합니다. "
Vicky Chijwani

3
@JeremyWest "하나는 힙의 상단 (어레이의 시작 부분)에서 시작하여 각 항목에서 siftUp을 호출하는 것입니다." -힙 맨 아래에서 시작한다는 의미입니까?
aste123

4
@ aste123 아니요, 작성된대로 정확합니다. 아이디어는 힙 속성을 만족하는 배열 부분과 배열의 정렬되지 않은 부분 사이에 장벽을 유지하는 것입니다. 처음부터 앞으로 이동하고 siftUp각 항목을 호출 하거나 끝에서 뒤로 이동하여 호출을 시작 siftDown합니다. 어떤 방법을 선택하든 배열의 정렬되지 않은 부분에서 다음 항목을 선택하고 적절한 작업을 수행하여 배열의 정렬 된 부분에서 유효한 위치로 이동합니다. 유일한 차이점은 성능입니다.
Jeremy West

2
이것은 내가 세계에서 어떤 질문에도 본 최고의 답변입니다. 그것은 잘 설명되었습니다, 나는 그것이 정말로 가능하다고 생각했습니다 ... 많은 감사합니다.
HARSHIL JAIN

314

분석이 정확합니다. 그러나 빡빡하지는 않습니다.

왜 힙을 만드는 것이 선형 작업인지 설명하는 것은 쉽지 않습니다. 더 잘 읽으십시오.

알고리즘에 대한 훌륭한 분석여기에서 볼 수 있습니다 .


주요 아이디어는 build_heap알고리즘에서 실제 heapify비용이 O(log n)모든 요소에 대한 것은 아니라는 것 입니다.

언제 heapify 호출, 실행 시간은 요소가 프로세스 종료하기 전에 트리에서 아래로 이동하는 방법까지에 따라 달라집니다. 즉, 힙의 요소 높이에 따라 다릅니다. 최악의 경우 요소가 리프 수준까지 내려갈 수 있습니다.

작업을 레벨별로 계산합니다.

최하위 레벨에는 2^(h)노드가 있지만 heapify이들 중 어느 것도 호출하지 않으므로 작업은 0입니다. 레벨 옆에 2^(h − 1)노드가 있으며 각 노드가 1 레벨 씩 아래로 이동할 수 있습니다. 바닥에서 3 층에는2^(h − 2) 노드가 있으며 각각 2 단계 씩 아래로 이동할 수 있습니다.

당신이 모든 heapify 작업을 볼 수있는 것은 아니지만 O(log n), 이것이 당신이 얻는 이유 O(n)입니다.


17
이것은 훌륭한 설명입니다 ... 그러나 왜 힙 정렬이 O (n log n)에서 실행됩니까? 힙 정렬에 동일한 추론이 적용되지 않는 이유는 무엇입니까?
hba

49
@hba 귀하의 질문에 대한 답변은 이 기사 에서이 이미지 를 이해하는 데 있습니다 . 입니다 함께 할 때 하지만, 함께 할 때 . (하나 힙 하나의 항목을 당기는) 실제 정렬 함께 수행되어야 하므로, 따라서이다 . HeapifyO(n)siftDownO(n log n)siftUpsiftUpO(n log n)
The111

3
맨 아래에있는 외부 문서의 직관적 인 설명이 정말 좋습니다.
Lukas Greblikas

1
@hba Jeremy West의 아래 답변은 귀하의 질문을보다 정교하고 이해하기 쉬운 자세로 다루며 The111의 의견 답변을 자세히 설명합니다.
cellepo

질문. i높이 h의 나무 바닥 에서 높이에있는 노드에 대한 # 비교는 2* log(h-i)비교를해야하며 @ The111도 고려해야합니다. 어떻게 생각해?
Sid

94

직관적으로 :

"복잡성이 O (nLog n)이어야합니다."우리가 "힙화"하는 각 항목에 대해 지금까지 힙의 각 레벨 (로그 n 레벨)에 대해 한 번 필터링해야 할 가능성이 있습니다. "

좀 빠지는. 로직은 엄격한 제한을 생성하지 않습니다. 각 힙 파이의 복잡성을 추정합니다. 상향식으로 제작 된 경우 삽입 (힙)이보다 작을 수 있습니다 O(log(n)). 과정은 다음과 같습니다.

(단계 1) n/2요소 힙의 아랫 줄에 간다. h=0따라서 heapify는 필요하지 않습니다.

(2 단계) 다음 요소는 맨 아래부터 1 ​​행으로 이동합니다. , heapify 필터는 1 레벨 아래로 내려갑니다.n/22h=1

(단계 i ) 다음 요소 는 맨 아래부터 행으로 올라 갑니다 . , heapify는 필터 레벨을 낮 춥니 다.n/2iih=ii

(Step log (n) ) 마지막 요소는 맨 아래부터 위로 올라 갑니다 . , heapify는 필터 레벨을 낮 춥니 다.n/2log2(n) = 1log(n)h=log(n)log(n)

주의 사항 : 1 단계 이후 1/2에 요소 중 하나 (n/2)가 이미 힙에 있으므로 heapify를 한 번 호출 할 필요조차 없습니다. 또한 루트라는 단일 요소 만 실제로 전체 log(n)복잡성을 유발한다는 점에 유의하십시오 .


이론적으로 :

Nsize의 힙을 빌드하는 총 단계 는 n수학적으로 작성할 수 있습니다.

높이 i에서 우리는 heapify를 호출 해야하는 요소가 있음을 보여 주었고 (위에서) heapify는 height 입니다 . 이것은 다음을 제공합니다.n/2i+1iO(i)

여기에 이미지 설명을 입력하십시오

마지막 합에 대한 해는 잘 알려진 기하 계열 방정식의 양변의 도함수를 취하면 찾을 수 있습니다.

여기에 이미지 설명을 입력하십시오

마지막으로 x = 1/2위의 방정식에 꽂으면 됩니다 2. 이것을 첫 번째 방정식에 꽂으면 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

따라서 총 단계 수는 크기입니다. O(n)


35

반복적으로 요소를 삽입하여 힙을 빌드하면 O (n log n)입니다. 그러나 임의의 순서로 요소를 삽입 한 다음 알고리즘을 적용하여 요소를 적절한 순서로 "힙니다"(물론 힙 유형에 따라 다름) 새 힙을보다 효율적으로 작성할 수 있습니다.

예는 http://en.wikipedia.org/wiki/Binary_heap , "힙 구축"을 참조하십시오 . 이 경우 기본적으로 트리의 맨 아래 레벨에서 작업하여 힙 조건이 충족 될 때까지 상위 및 하위 노드를 교체합니다.


12

이미 훌륭한 답변이 있지만 약간의 시각적 설명을 추가하고 싶습니다.

여기에 이미지 설명을 입력하십시오

이제 이미지를 살펴보고, 거기에
n/2^1 녹색 노드높이를 0 (여기 = 12 2분의 23)를
n/2^2 빨간색 노드높이 1 (여기 = 6 4분의 23)
n/2^3 블루 노드높이 2 (여기 8분의 23 = 3)
n/2^4 보라색 노드높이 3 (여기서 = 2 16분의 23)
그래서 거기에 n/2^(h+1)높이에 대한 노드 시간
의 시간 복잡도를 찾으려면이 셀 수 있습니다 작업 수행의 양 또는 반복의 최대없이 수행 각 노드를
지금은 발견 할 수있는 각 노드 캔 수행 (최대) 반복 == 노드 높이

Green  = n/2^1 * 0 (no iterations since no children)  
red    = n/2^2 * 1 (heapify will perform atmost one swap for each red node)  
blue   = n/2^3 * 2 (heapify will perform atmost two swaps for each blue node)  
purple = n/2^4 * 3 (heapify will perform atmost three swaps for each purple node)   

따라서 높이 h가 있는 모든 노드의 최대 작업 수는 n / 2 ^ (h + 1) * h입니다.

이제 총 작업은

->(n/2^1 * 0) + (n/2^2 * 1)+ (n/2^3 * 2) + (n/2^4 * 3) +...+ (n/2^(h+1) * h)  
-> n * ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

이제 h의 모든 값에 대해 시퀀스

-> ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 


따라서 1을 초과하지 않습니다. 따라서 시간 복잡도는힙을 구축하기 위해 O (n) 을초과하지 않습니다.


7

우리가 알고있는 바와 같이, 힙의 높이 인 로그 (N) N elements.Lets의 총 개수로 표현하고, 시간
   후, 최종 레벨 (AT 요소 우리 heapify 작업을 수행 할 때 , h는 ) 심지어 단일 이동하지 단계.
   제 마지막 레벨 (AT 요소 수 의 H-1 )은 2 H-1 그들은 최대로 이동할 수 1 개 (heapify시) 수준.
   비슷하게, i th 레벨에는 hi 위치를 이동할 수 있는 2 개의 i 요소가 있습니다 .

그러므로 총 이동 횟수 = S = 2 h * 0 + 2 h-1 * 1 + 2 h-2 * 2 + ... 2 0 * h

                                               S = 2 시간 {1/2 + 2/2 2 + 3/2 3 + ... h / 2 시간 } ----------------------- -------------------------- 1
이것은 2 S / 2 = 2 h {1/2 로이 나누기를 해결하기 위해 AGP 시리즈입니다. 2 + 2/2 3 h + 1 {1 / 2 + 1 / 2 2 + 1/2 3 + ... + 1/2 h + h / 2 h + 1 } 이제 1 / 2 + 1 / 2 2 + 1/2 3 + ... + 1/2 h 는 합이보다 작은 GP 를 감소시킵니다
                                                + ... h / 2 h + 1 } --------------------------------- ---------------- 2
식 감산 2 에서 1 주는
                                               S / 2 = 2 H {1 / 2 + 1 / 2 2 + 2 3 + ... + 1 / 2 시간 + h / 2 시간 +1 }
                                               S = 2 1 (h가 무한대 인 경우 합은 1이됩니다). 추가 분석에서 1의 합에 대한 상한을 취합시다. 이것은 S = 2 h + 1 {1 + h / 2 h + 1 }                     = 2 h + 1 + h ~ 2 h + hh = log 로 나타 냅니다. (n) , 2 시간 = n



                    


따라서 S = n + log (n)
T (C) = O (n)


6

힙을 구축하는 동안 상향식 접근 방식을 사용한다고 가정 해 봅시다.

  1. 각 요소를 가져 와서 하위 요소와 비교하여 쌍이 힙 규칙을 준수하는지 확인하십시오. 따라서 잎은 힙에 무료로 포함됩니다. 아이들이 없기 때문입니다.
  2. 위로 올라가면 나뭇잎 바로 위의 노드에 대한 최악의 시나리오는 1 비교입니다 (최대 한 세대의 어린이와 비교할 때).
  3. 더 나아가서 직계 부모는 최대 두 세대의 자녀와 비교할 수 있습니다.
  4. 같은 방향으로 계속하면 최악의 시나리오에서 루트에 대한 log (n) 비교가 이루어집니다. 직계 자녀에 대해서는 log (n) -1, 직계 자녀에 대해서는 log (n) -2 등.
  5. 요약하자면 log (n) + {log (n) -1} * 2 + {log (n) -2} * 4 + ..... + 1 * 2 ^ {( logn) -1} 이것은 O (n)에 지나지 않습니다.

2

힙을 빌드하는 경우, 높이 logn -1 에서 시작합니다 (여기서 logn은 n 요소의 트리 높이입니다). 높이 'h'에있는 각 요소에 대해 최대 up (logn -h) 높이로 내려갑니다.

    So total number of traversal would be:-
    T(n) = sigma((2^(logn-h))*h) where h varies from 1 to logn
    T(n) = n((1/2)+(2/4)+(3/8)+.....+(logn/(2^logn)))
    T(n) = n*(sigma(x/(2^x))) where x varies from 1 to logn
     and according to the [sources][1]
    function in the bracket approaches to 2 at infinity.
    Hence T(n) ~ O(n)

1

연속 삽입은 다음과 같이 설명 할 수 있습니다.

T = O(log(1) + log(2) + .. + log(n)) = O(log(n!))

근사 스탈링함으로써, n! =~ O(n^(n + O(1)))그러므로T =~ O(nlog(n))

이것이 도움이되기를 바랍니다. 최상의 방법 O(n)은 주어진 세트에 대해 빌드 힙 알고리즘을 사용하는 것입니다 (순서는 중요하지 않습니다).


1

기본적으로 힙을 빌드하는 동안 리프가 아닌 노드에서만 작업이 수행됩니다 ... 그리고 작업은 힙 조건을 충족시키기 위해 스왑 다운하는 양입니다. 즉, 최악의 경우 양은 높이에 비례합니다 문제의 모든 복잡성에서 모든 비 리프 노드의 높이의 합에 비례합니다. (2 ^ h + 1-1) -h-1 = nh-1 = 의 위에)


1

@bcorso는 이미 복잡성 분석의 증거를 보여주었습니다. 그러나 여전히 복잡한 학습을 ​​배우기 위해 이것을 추가해야합니다.

원래 실수의 기초는 "힙에 삽입하는 데 O (log n) 시간이 걸립니다"라는 문장의 의미를 잘못 해석했기 때문입니다. 힙에 삽입하는 것은 실제로 O (log n)이지만 , 삽입 중 n이 힙의 크기임을 인식해야 합니다 .

n 개의 객체를 힙에 삽입하는 맥락에서, i 번째 삽입의 복잡성은 O (log n_i)이며, 여기서 n_i는 삽입 i에서와 같이 힙의 크기입니다. 마지막 삽입에만 O (log n)의 복잡성이 있습니다.


1

힙에 N 개의 요소 가 있다고 가정합니다 . 그러면 높이는 Log (N)입니다.

이제 당신은 그 복잡성은 것, 다른 요소를 삽입 할 : 로그 (N)을 , 우리는 모든 방법을 비교해야 UP 루트를.

이제 N + 1 요소 가 있고 높이 = Log (N + 1)

유도 기법을 사용하면 삽입의 복잡성이 ∑logi 임을 증명할 수 있습니다 .

이제

로그 a + 로그 b = log ab

이것은 다음과 같이 단순화됩니다. ∑logi = log (n!)

실제로는 O (NlogN)입니다

그러나

우리는 모든 경우에 우리가 정상에 도달하지 않기 때문에 여기에서 뭔가 잘못하고 있습니다. 따라서 대부분의 시간을 실행하는 동안 우리는 트리의 절반을 넘지 않을 것입니다. 이 답변은 위의 답변에 제공된 수학을 사용하여 더 엄격한 바인딩을 갖도록 최적화 할 수 있습니다.

이 실현은 힙에 대한 세부 사항 및 실험 후에 나에게 왔습니다.


0

나는 Jeremy West의 설명을 정말로 좋아합니다 .... 이해하기 쉬운 또 다른 접근법은 여기에 있습니다 http://courses.washington.edu/css343/zander/NotesProbs/heapcomplexity

buildheap의 사용은 heapify에 의존하고 모든 노드의 높이의 합에 의존하는 shiftdown 접근 방식이 사용되기 때문에 사용됩니다. 따라서 S = i에서 0으로의 i = h에서 (2 ^ i * (hi))의 합으로 주어진 노드의 높이의 합을 찾으려면, 여기서 h = logn은 트리 풀기의 높이입니다. n = 2 ^ (h + 1)-1 s = n-h-1 = n-logn-1 s = O (n)이므로 s = 2 ^ (h + 1)-1-(h + 1) 따라서 buildheap의 복잡성은 O (n)입니다.


0

"힙 힙의 모든 노드 높이의 합을 계산하여 빌드 힙의 선형 시간 범위를 표시 할 수 있습니다. 이는 최대 파선 수입니다. N = 2 ^ ( h + 1) – 1 노드, 노드 높이의 합은 N – H – 1입니다. 따라서 O (N)입니다. "


0

O의 증거 (n)

증거는 공상적이지 않으며 매우 간단합니다. 나는 완전한 이진 트리의 경우만을 증명했으며 결과는 완전한 이진 트리로 일반화 될 수 있습니다.


0

각 노드가 취할 수있는 최대 이동을 파악하여 힙 빌드에 대한 런타임을 얻습니다. 따라서 각 행에 몇 개의 노드가 있고 각 노드가 얼마나 멀리 떨어져 있는지 알아야합니다.

루트 노드에서 시작하여 다음 각 행은 이전 행보다 두 배의 노드를 가지므로 노드가 없을 때까지 얼마나 자주 노드 수를 두 배로 늘릴 수 있는지에 따라 트리의 높이를 얻습니다. 또는 수학적으로 트리의 높이는 log2 (n)이며 n은 배열의 길이입니다.

하나의 행에서 노드를 계산하여 뒤에서 시작하면 n / 2 개의 노드가 맨 아래에 있음을 알 수 있으므로 2로 나누어 이전 행을 얻습니다.

이를 바탕으로 (0 * n / 2) + (1 * n / 4) + (2 * n / 8) + ... + (log2 (n) * 1)에 대한 공식을 얻습니다.

마지막 마비의 용어는 루트에있는 하나의 노드에 트리의 높이를 곱한 값이며, 첫 번째 마비의 용어는 맨 아래 행의 모든 ​​노드에 이동할 수있는 길이를 곱한 값입니다. 스마트에서 동일한 공식 : 여기에 이미지 설명을 입력하십시오

수학

n을 2로 가져 오면 2 * n, 2를 버릴 수 있습니다. 상수는 상수이므로 우리는 Siftdown 방식의 최악의 런타임을 갖습니다. n.


-6

당신이 실수하고 있다고 생각합니다. 이것을보십시오 : http://golang.org/pkg/container/heap/ 힙을 구축하는 것은 O (n)이 아닙니다. 그러나 삽입은 O (lg (n)입니다. 힙 크기를 b / c로 설정하면 힙이 공간을 할당하고 데이터 구조를 설정 해야하는 경우 초기화가 O (n)이라고 가정합니다. 힙에 예, 각 삽입은 lg (n)이고 n 개의 항목이 있으므로 u * n (n)을 얻습니다.


2
아니 빡빡하지 않습니다. 빌드 힙 수율의 O의 엄격한 분석 (N)
엠레 nevayeshirazi

추정치 인 것 같습니다. 그가 언급 한 기사의 인용문은 "직관은 heapify에 대한 대부분의 호출이 매우 짧은 힙에 있다는 것입니다." 아마도 큰 힙의 경우 최악의 시나리오는 일반적으로 O (n)에 가까워도 여전히 O (n * lg (n))입니다. 그러나 나는 틀릴 수 있었다
Mike Schachter

예, 저의 직관적 인 대답이지만 위키피디아 상태와 같은 참조는 "n 개의 요소를 가진 힙은 O (n)에서 상향식으로 구성 할 수 있습니다."
GBa

1
완전히 정렬 된 데이터 구조를 생각하고있었습니다. 힙의 특정 속성을 잊었습니다.
Mike Schachter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.