이진 검색 트리와 이진 힙의 차이점은 무엇입니까?


91

이 두 가지는 매우 유사 해 보이며 거의 동일한 구조를 가지고 있습니다. 차이점이 뭐야? 각각의 다른 작업에 대한 시간 복잡성은 무엇입니까?

답변:


63

힙은 상위 레벨의 요소가 하위 레벨의 요소보다 크거나 (최대 힙의 경우) 더 작거나 (최소 힙의 경우) 보장하는 반면 BST는 순서 ( "왼쪽"에서 "오른쪽")를 보장합니다. 정렬 된 요소를 원하면 BST로 이동하십시오. 단테에 의해 괴짜 아니에요

힙은 findMin / findMax (O (1))에서 더 나은 반면 BST는 모든 발견 (O (logN))에서 우수합니다. 두 구조 모두에 대한 삽입은 O (logN)입니다. findMin / findMax에만 관심이있는 경우 (예 : 우선 순위 관련) 힙을 사용하십시오. 모든 것을 정렬하려면 BST로 이동하십시오.

xysun 제작


BST가 findMin 및 findMax에서 더 좋다고 생각합니다. stackoverflow.com/a/27074221/764592
Yeo

10
나는 이것이 일반적인 오해라고 생각합니다. 이진 트리는 Yeo가 지적한대로 최소 및 최대를 찾기 위해 쉽게 수정할 수 있습니다. 이것은 실제로 힙 의 제한 사항 입니다. 유일한 효율적인 찾기는 최소 또는 최대입니다. 힙의 진정한 장점은 O (1) 평균 삽입 내가 설명과 같이 stackoverflow.com/a/29548834/895245
치로 틸리新疆改造中心法轮功六四事件

이 비디오 에 따르면 , 큰 값이 낮은 값의 자손이 아닌 한 낮은 값에서 더 큰 값을 가질 수 있습니다.
whoan

힙은 리프에서 루트로 정렬되고 BST는 왼쪽에서 오른쪽으로 정렬됩니다.
Deep Joshi

34

이진 검색 나무바이너리 힙 트리 기반 데이터 구조입니다.

힙은 노드가 자식보다 우선 순위를 갖도록 요구합니다. 최대 힙에서 각 노드의 하위는 자체보다 작아야합니다. 이것은 최소 힙의 반대입니다.

이진 최대 힙

이진 검색 트리 (BST)는 형제 노드 중 특정 순서 (사전 순서, 순서, 순서)를 따릅니다. 힙과 달리 트리 정렬 해야합니다 .

이진 검색 트리

O(logn)
O(1)O(logn)


1
O(logn)

32

요약

          Type      BST (*)   Heap
Insert    average   log(n)    1
Insert    worst     log(n)    log(n) or n (***)
Find any  worst     log(n)    n
Find max  worst     1 (**)    1
Create    worst     n log(n)  n
Delete    worst     log(n)    log(n)

이 표의 모든 평균 시간은 삽입을 제외하고 최악의 시간과 동일합니다.

  • *:이 답변의 모든 곳에서 BST == 균형이 잡힌 BST는 언밸런스가 무증상이기 때문에
  • **:이 답변에 설명 된 사소한 수정 사용
  • ***: log(n)포인터 트리 힙, n동적 배열 힙

BST에 비해 이진 힙의 장점

이진 힙에 비해 BST의 장점

  • 임의의 요소 검색은 O(log(n))입니다. 이것이 BST의 킬러 기능입니다.

    힙의 O(n)경우 가장 큰 요소 인를 제외하고 일반적으로입니다 O(1).

BST보다 힙의 "거짓"장점

평균 이진 힙 삽입은 O(1)

출처 :

직관적 인 주장 :

  • 최하위 트리 레벨은 최상위 레벨보다 기하 급수적으로 더 많은 요소를 가지므로 새로운 요소는 거의 최하단에 갈 것입니다
  • 힙 삽입 은 하단 에서 시작하고 BST는 상단에서 시작해야합니다.

이진 힙에서, 주어진 인덱스에서 값을 증가시키는 것도 O(1)같은 이유입니다. 그러나 그렇게하려면 힙 작업에 대한 추가 색인을 최신 상태로 유지하고 싶을 것입니다 https : //.com/questions/17009056/how-to-implement-ologn-decrease Dijkstra와 같은 키 -최저-힙-기반 우선 순위-큐에 대한 키-동작 . 추가 비용없이 가능합니다.

실제 하드웨어에서 GCC C ++ 표준 라이브러리 삽입 벤치 마크

삽입 시간에 대해 올바른지 확인하기 위해 C ++ std::set( Red-black tree BST ) 및 std::priority_queue( dynamic array heap ) 삽입을 벤치마킹했으며 이것이 내가 얻은 것입니다.

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

  • 벤치 마크 코드
  • 줄거리 스크립트
  • 플롯 데이터
  • CPU가 장착 된 Lenovo ThinkPad P51 랩탑에서 Ubuntu 19.04, GCC 8.3.0 테스트 : Intel Core i7-7820HQ CPU (4 코어 / 8 스레드, 2.90GHz 기본, 8MB 캐시), RAM : Samsung M471A2K43BB1-CRC (2x 16GiB) , 2400Mbps), SSD : Samsung MZVLB512HAJQ-000L7 (512GB, 3,000MB / s)

그래서 분명히 :

gem5의 GCC C ++ 표준 라이브러리 삽입 벤치 마크

gem5 는 전체 시스템 시뮬레이터이므로로 정확한 무한 시계를 제공합니다 m5 dumpstats. 그래서 개별 인서트의 타이밍을 추정하는 데 사용하려고했습니다.

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

해석:

  • 힙은 여전히 ​​일정하지만 이제 몇 줄이 있고 더 높은 줄이 더 희박하다는 것을 더 자세히 알 수 있습니다.

    이는 더 높은 삽입을 위해 수행되는 메모리 액세스 대기 시간과 일치해야합니다.

  • TODO 나는 BST가 대수적이고 다소 일정하게 보이지 않기 때문에 BST를 완전히 해석 할 수는 없습니다.

    그러나이 세부 사항을 통해 몇 가지 뚜렷한 선을 볼 수도 있지만 그 선이 무엇인지 잘 모르겠습니다. 상단을 삽입하므로 하단 선이 더 얇을 것으로 예상됩니까?

aarch64 HPI CPU 에서이 Buildroot 설정 으로 벤치마킹했습니다 .

어레이에서 BST를 효율적으로 구현할 수 없음

힙 작업은 단일 트리 분기 만 버블 업하거나 다운해야하므로 O(log(n))최악의 경우 스왑이 O(1)평균입니다.

BST의 균형을 유지하려면 트리 회전이 필요합니다. 이로 인해 다른 요소의 상단 요소가 변경 될 수 있으며 전체 배열을 ( O(n)) 주위로 이동해야합니다 .

어레이에서 힙을 효율적으로 구현할 수 있습니다

부모 및 자식 인덱스는 여기에 표시된 것처럼 현재 인덱스에서 계산할 수 있습니다 .

BST와 같은 밸런싱 작업이 없습니다.

삭제 작업은 하향식이므로 가장 걱정되는 작업입니다. 그러나 여기에 설명 된대로 항상 힙의 단일 분기를 "통과"하여 수행 할 수 있습니다 . 힙은 항상 균형이 잘 잡혀 있기 때문에 O (log (n)) 최악의 경우가 발생합니다.

제거 할 때마다 단일 노드를 삽입하는 경우 힙이 삭제 한대로 제공하는 점근 적 O (1) 평균 삽입의 이점을 잃어 BST를 사용할 수도 있습니다. 그러나 Dijkstra는 제거 할 때마다 노드를 여러 번 업데이트하므로 괜찮습니다.

동적 배열 힙과 포인터 트리 힙

힙은 포인터 힙 위에 효율적으로 구현 될 수 있습니다. https://stackoverflow.com/questions/19720438/is-it-possible-to-make-efficient-pointer-based-binary-heap-implementations

동적 배열 구현은보다 공간 효율적입니다. 각 힙 요소에 다음에 대한 포인터 만 포함되어 있다고 가정하십시오 struct.

  • 트리 구현은 각 요소에 대해 부모, 왼쪽 자식 및 오른쪽 자식의 세 가지 포인터를 저장해야합니다. 따라서 메모리 사용량은 항상 4n(3 트리 포인터 + 1 struct포인터)입니다.

    트리 BST는 블랙-레드-니스와 같은 추가 밸런싱 정보도 필요합니다.

  • 동적 배열 구현은 2n배가 된 직후 의 크기 일 수 있습니다 . 따라서 평균적으로는입니다 1.5n.

반면, 배킹 동적 배열의 크기를 두 배로 복사하면 O(n)최악의 경우 가 걸리고 트리 힙은 각 노드에 대해 새로운 작은 할당을 수행 하기 때문에 트리 힙은 최악의 경우 삽입이 더 좋습니다 .

그럼에도 불구하고 배킹 어레이 배가는 O(1)상각되므로 최대 대기 시간 고려 사항이 적용됩니다. 여기에 언급했습니다 .

철학

  • BST는 부모와 모든 자손 사이의 전역 속성을 유지합니다 (왼쪽이 작고 오른쪽이 더 큼).

    BST의 최상위 노드는 중간 요소로, 유지하려면 글로벌 지식이 필요합니다 (작고 큰 요소가 몇 개인 지 알고 있음).

    이 전역 속성은 유지 관리하는 데 비용이 많이 들고 (log n insert) 더 강력한 검색 (log n search)을 제공합니다.

  • 힙은 부모와 직계 자녀 (부모> 자녀) 사이에 지역 재산을 유지합니다.

    힙의 최상위는 큰 요소이며, 유지하기 위해 로컬 지식 만 있으면됩니다 (부모를 알고 있음).

이중 연결 목록

이중 연결 목록은 첫 번째 항목이 가장 높은 힙의 하위 집합으로 볼 수 있으므로 여기에서도 비교해 보겠습니다.

  • 삽입:
    • 위치:
      • 이중 연결 목록 : 삽입 된 항목은 해당 요소에 대한 포인터 만 있으므로 첫 번째 또는 마지막이어야합니다.
      • 이진 힙 : 삽입 된 항목은 어느 위치에서나 끝날 수 있습니다. 링크 된 목록보다 덜 제한적입니다.
    • 시각:
      • 이중 연결 목록 : O(1)항목에 대한 포인터가 있기 때문에 최악의 경우이며 업데이트는 정말 간단합니다.
      • 이진 힙 : O(1)평균이므로 링크 된 목록보다 나쁩니다. 보다 일반적인 삽입 위치에 대한 절충.
  • 검색 : O(n)둘 다

이를위한 사용 사례는 힙 키가 현재 타임 스탬프 인 경우입니다.이 경우 새 항목이 항상 목록의 시작 부분으로 이동합니다. 따라서 정확한 타임 스탬프를 모두 잊고 목록의 위치를 ​​우선 순위로 유지하면됩니다.

이것은 LRU 캐시 를 구현하는 데 사용될 수 있습니다 . 마찬가지로 익스트라 같은 힙 애플리케이션에 신속하게 업데이트 할 노드 찾기 위해 목록의 해당 노드로 키에서 추가 해시 맵을 유지하기를 원할 것입니다.

다른 균형 BST의 비교

지금까지 본 "균형 BST"로 분류되는 모든 데이터 구조에 대한 점근 적 삽입 및 찾기 시간은 동일하지만 BBST마다 서로 다른 절충점이 있습니다. 나는 이것을 아직 완전히 연구하지는 않았지만 여기에 이러한 절충 사항을 요약하는 것이 좋습니다.

  • 레드 블랙 트리 . 2019 년 현재 가장 일반적으로 사용되는 BBST 인 것처럼 보입니다 (예 : GCC 8.3.0 C ++ 구현에서 사용되는 것임).
  • AVL 트리 . BST보다 약간 균형이 잘 잡혀 있으므로 약간 더 비싼 찾기 비용으로 찾기 지연 시간이 더 나을 수 있습니다. Wiki 요약 : "AVL 트리는 종종 동일한 작업 집합을 지원하고 기본 작업에 [동일한] 시간이 걸리기 때문에 종종 레드-블랙 트리와 비교됩니다. 조회 집약적 인 애플리케이션의 경우 AVL 트리는 레드-블랙 트리보다 빠릅니다. 빨강-검정색 나무와 유사하게 AVL 나무는 높이가 균형을 이룹니다. 일반적으로 mu <1/2의 무게 균형이나 균형이 맞지 않습니다. 즉, 형제 노드는 엄청나게 클 수 있습니다. 다른 수의 자손. "
  • WAVL . 원래의 논문은 재조정 및 회전 작업에 대한 경계의 측면에서 해당 버전의 장점을 언급하고있다.

또한보십시오

CS에 대한 비슷한 질문 : 이진 검색 트리와 이진 힙의 차이점은 무엇입니까?


1
좋은 대답입니다. 힙의 일반적인 적용은 중간 값, 최소값, 상위 k 요소입니다. 이러한 가장 일반적인 작업의 경우 min을 제거한 다음 insert를 입력합니다 (일반적으로 순수한 삽입 작업이 거의없는 작은 힙이 있음). 실제로 이러한 알고리즘의 경우 BST보다 성능이 뛰어나지 않습니다.
yura

1
탁월한 답변 !!! 기본 힙 구조로 deque를 사용하면 크기 조정 시간을 대폭 줄일 수 있지만 여전히 청크에 대한 포인터 배열을 (더 작은) 재 할당해야하기 때문에 O (n) 최악의 경우입니다.
Bulat

13

데이터 구조를 통해 관심 수준을 구분해야합니다.

  1. 이 질문 의 추상 데이터 구조 (저장된 객체, 작업)는 다릅니다. 하나는 우선 순위 큐를 구현하고 다른 하나는 세트를 구현합니다. 우선 순위 큐는 임의의 요소를 찾는 데 관심이 없으며 우선 순위가 가장 큰 요소 만 찾습니다.

  2. 구조 의 구체적인 구현 . 첫눈에 둘 다 (이진) 나무이지만 구조적 특성이 다릅니다. 키의 상대적 순서와 가능한 전역 구조가 다릅니다. ( BST키워드는 왼쪽에서 오른쪽으로, 힙에서는 위에서 아래로 정렬됩니다.) IPlant가 올바르게 말하면 힙도 "완료"해야합니다.

  3. 저수준 구현 에는 최종 차이가 있습니다. (불균형) 이진 검색 트리에는 포인터를 사용하는 표준 구현이 있습니다. 반대로 바이너리 힙은 배열을 사용하여 효율적으로 구현됩니다 (정확히 제한된 구조로 인해).


1

이전 답변 외에도 힙에는 힙 구조 속성이 있어야합니다. 나무는 꽉 차 있어야하며 항상 가득 찰 수없는 최하단 레이어는 간격없이 가장 왼쪽에서 오른쪽으로 채워야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.