해시 테이블에 비해 이진 검색 트리의 장점


101

해시 테이블에 비해 이진 검색 트리의 장점은 무엇입니까?

해시 테이블은 Theta (1) 시간의 모든 요소를 ​​조회 할 수 있으며 요소를 추가하는 것만 큼 쉽습니다 ....하지만 그 반대 방향으로가는 이점이 확실하지 않습니다.


해시 테이블의 경우 find () insert () 및 remove ()의 실행 시간은 얼마입니까? theta (1) theta (1) 및 theta (1) 맞습니까?
헌신

8
거의 항상 그렇습니다. 충돌이 많이 발생하면 그 시간이 O (n)까지 증가 할 수 있습니다.
Christian Mann

1
이 시간은 해싱 함수에 따라 다릅니다. 이상한 이유로 O (1)이 아니라면 분명히 작업은 해시 함수가 실행되는 효율성의 최소 한계를 갖습니다.
Christian Mann

BST의 가장 큰 장점은 정렬 된 데이터 구조라는 것입니다. 여기에 이미 나열된 자세한 사용 사례 .
Yuantao

답변:


93

이진 검색 트리 (참조 기반)는 메모리 효율적입니다. 필요한 것보다 더 많은 메모리를 예약하지 않습니다.

예를 들어 해시 함수에 범위가있는 R(h) = 0...100경우 요소 20 개를 해싱하는 경우에도 100 (포인터) 요소의 배열을 할당해야합니다. 이진 검색 트리를 사용하여 동일한 정보를 저장하는 경우 필요한만큼의 공간과 링크에 대한 일부 메타 데이터 만 할당합니다.


33
해시 함수 출력의 전체 범위가 배열에 있어야한다는 것은 사실이 아닙니다. 해시 값은 배열의 길이로 간단히 수정하여 더 작은 배열을 허용 할 수 있습니다. 물론 추가되는 요소의 최종 개수를 알 수 없으므로 해시 테이블은 여전히 ​​필요한 것보다 더 많은 공간을 할당 할 수 있습니다. 이진 검색 트리는 많은 메모리 또는 그 이상을 낭비 할 수 있습니다. 연결된 구현은 요소 당 최소 2 개의 추가 포인터 (부모 포인터를 사용하는 경우 3 개)를위한 공간이 필요하며 배열 기반 BST는 트리의 채워지지 않은 부분에 대해 많은 메모리를 낭비 할 수 있습니다.
Solaraeus

4
@Solaraeus : 배열 기반 BST는 해시 테이블과 비교하는 것이 가장 좋으며 해시 테이블보다 낭비가 없습니다. 전체 테이블을 재 계산하는 것과 비교하여 메모리 복사본보다 약간 더 많은 BST를 확장 할 수도 있습니다.
Guvante 2012 년

125

다른 누구도 지적하지 않은 한 가지 장점은 이진 검색 트리를 사용하여 범위 검색을 효율적으로 수행 할 수 있다는 것입니다.

내 생각을 설명하기 위해 극단적 인 사례를 만들고 싶습니다. 키가 0에서 5000 사이 인 모든 요소를 ​​가져오고 싶다고 가정 해 보겠습니다. 실제로 그러한 요소는 하나만 있고 키가 범위에없는 10000 개의 다른 요소가 있습니다. BST는 답을 얻을 수없는 하위 트리를 검색하지 않기 때문에 범위 검색을 매우 효율적으로 수행 할 수 있습니다.

그러나 해시 테이블에서 범위 검색을 어떻게 수행 할 수 있습니까? O (n) 인 모든 버킷 공간을 반복해야하거나 1,2,3,4 ... 개가 최대 5000 개까지 존재하는지 찾아야합니다. (0에서 5000 사이의 키는 무한 세트 인 것은 어떻습니까? 예를 들어 키는 십진수 일 수 있습니다)


11
BST는 범위 검색을 효율적으로 수행합니다! 나에게 이것은 실용적이고 알고리즘적인 접근 방식의 가장 좋은 대답입니다.
ady dec

4
와우 이것은 나무가 데이터베이스와 관련된 이유를 설명합니다. 이러한 이점은 키 기반 필터링을 수행해야 할 때 가장 잘 나타납니다. 해시 맵을 사용하면 "1000에서 3290 사이의 키가있는 모든 항목 찾기"를 해결하기 위해 모든 키를 반복해야합니다.
Dmitry

77

이진 트리의 한 가지 "장점"은 모든 요소를 ​​순서대로 나열하기 위해 순회 할 수 있다는 것입니다. 이것은 해시 테이블로는 불가능하지 않지만 해시 구조로 설계하는 정상적인 작업은 아닙니다.


3
어떤 순서 로든 순회하는 것은 아마도 해시 테이블에서 의미가 없을 것입니다.
FrustratedWithFormsDesigner

2
@FrustratedWithFormsDesigner. 정렬 된 선형 해시 테이블
NealB 2010

링크 주셔서 감사합니다. 정말 멋진 아이디어입니다! 나는 그 구현을 보거나 사용한 적이 없다고 생각합니다 (적어도 고의적으로).
FrustratedWithFormsDesigner


51

다른 모든 좋은 댓글 외에도 :

일반적으로 해시 테이블은 이진 트리에 비해 적은 메모리 읽기가 필요한 캐시 동작이 더 좋습니다. 해시 테이블의 경우 일반적으로 데이터를 보유하는 참조에 액세스하기 전에 단일 읽기만 발생합니다. 이진 트리가 균형 잡힌 변형이라면 어떤 상수 k에 대해 k * lg (n) 메모리 읽기 순서의 무언가가 필요합니다 .

반면에 적이 당신의 해시 함수를 알고 있다면 적이 당신의 해시 테이블을 강제로 충돌을 일으키고 성능을 크게 저하시킬 수 있습니다. 해결 방법은 패밀리에서 임의로 해시 함수를 선택하는 것이지만 BST에는 이러한 단점이 없습니다. 또한 해시 테이블 압력이 너무 커지면 비용이 많이 드는 작업이 될 수있는 해시 테이블을 확대하고 재 할당하는 경향이 있습니다. BST는 여기에서 더 간단한 동작을 가지며 갑자기 많은 데이터를 할당하고 재해 싱 작업을 수행하지 않는 경향이 있습니다.

트리는 궁극적 인 평균 데이터 구조 인 경향이 있습니다. 목록 역할을 할 수 있고 병렬 작업을 위해 쉽게 분할 할 수 있으며 O (lg n) 순서로 빠른 제거, 삽입 및 조회가 가능합니다 . 그들은 특별히 잘하지 않지만 지나치게 나쁜 행동도 없습니다.

마지막으로, BST는 해시 테이블에 비해 (순수한) 기능적 언어로 구현하기가 훨씬 쉽고 파괴적인 업데이트를 구현할 필요가 없습니다 ( 위의 Pascal 의 지속성 인수).


3
BSTs are much easier to implement in (pure) functional languages compared to hash-tables- 정말? 나는 지금 기능적 언어를 배우고 싶다!
nawfal

1
해시 테이블은 기능적 언어로 지속적이어야합니다. 이것은 종종 구현을 복잡하게 만듭니다.
I GIVE CRAP ANSWERS

정교하게 말하면, 함수형 언어로 대통령 데이터 구조를 만드는 경우 실제로 수행하는 작업은 어셈블리에서와 동일한 코드를 작성하는 것입니다. 단, 각 작업에서 메모리 / 레지스터 배열을 명시 적으로 변환하거나 가장하기 위해 서버와 대화하는 것을 제외하고는 하기 위해서. 나는 당신의 상태를 인식하고 있지만 올바르게 수행되면 명령 적 접근 방식과 동형입니다 (실생활에서 각 변환에 대해 많은 양의 데이터를 현실적으로 복사 할 수 없으므로 속임수가 필요합니다).
Dmitry

27

해시 테이블에 비해 이진 트리의 주요 장점은 이진 트리가 해시 테이블로 (쉽고 빠르게) 수행 할 수없는 두 가지 추가 작업을 제공한다는 것입니다.

  • 임의의 키 값 (또는 가장 가까운 위 / 아래)에 가장 가까운 (반드시 같지는 않음) 요소 찾기

  • 정렬 된 순서로 트리의 내용을 반복합니다.

둘은 연결되어 있습니다. 이진 트리는 내용을 정렬 된 순서로 유지하므로 정렬 된 순서가 필요한 작업을 쉽게 수행 할 수 있습니다.


BST는 정확히 일치하는 것이 존재하지 않는 경우에만 가장 가까운 일치를 찾습니다. 루트 자체에서 정확히 일치하는 것을 찾으면 어떨까요?
developer747

2
@ developer747 : 다음으로 가장 가까운 아래 및 위는 왼쪽 하위 트리의 가장 오른쪽 리프와 오른쪽 하위 트리의 가장 왼쪽 리프입니다.
크리스 도드

16

(균형) 이진 검색 트리는 점근 적 복잡성이 실제로 상한이라는 장점이 있지만 해시 테이블의 "상수"시간은 상각 된 시간입니다. 부적합한 해시 함수가 있으면 결국 선형 시간으로 저하 될 수 있습니다. , 일정하지 않습니다.


3
이 지점을 집으로 옮기기 위해 컬렉션에 단 하나의 키에 대한 많은 사본이 포함되어있는 경우가 퇴화됩니다. BST에서 삽입은 O (log n), 해시 테이블에서 삽입은 O (n)
SingleNegationElimination 2010

2
해시 테이블에 키 1 개의 복사본이 많이 포함되어있는 경우 삽입은 O (n)이 아니라 (여전히) O (1)입니다. 해시 테이블의 문제 는 동일한 해시를 가진 여러 키 가있을 때 입니다. 충돌이 많을 때 다른 해시 기능으로 전환하는 동적 해시 체계를 사용하면이를 방지 할 수 있습니다.
Chris Dodd

불균형 트리는 목록으로 퇴화 될 수 있으며 O (n) 조회도 가질 수 있습니다.
awiebe

9

해시 테이블은 처음 생성 될 때 더 많은 공간을 차지합니다.-아직 삽입되지 않은 요소 (삽입 여부에 관계없이)에 대해 사용 가능한 슬롯이 있으며 이진 검색 트리는 필요한만큼만 커집니다. 있다. 해시 테이블이 더 많은 공간을 필요로 할 때 또 다른 구조로 확장하는 것은 많은 시간이 소요될 수 있지만 그 구현에 달려 있습니다.


8

이진 검색 트리는 새 트리가 반환되지만 이전 트리는 계속 존재 하는 영구 인터페이스 로 구현 될 수 있습니다 . 신중하게 구현 된 이전 트리와 새 트리는 대부분의 노드를 공유합니다. 표준 해시 테이블로는이를 ​​수행 할 수 없습니다.


6

이진 트리는 검색 및 삽입 속도가 더 느리지 만 중위 순회의 매우 멋진 기능이 있습니다. 이는 기본적으로 트리의 노드를 정렬 된 순서로 반복 할 수 있음을 의미합니다.

해시 테이블의 항목을 반복하는 것은 모두 메모리에 흩어져 있기 때문에 별 의미가 없습니다.


6

에서 코딩 인터뷰를 크래킹, 6 판

BST (균형 이진 검색 트리)를 사용하여 해시 테이블을 구현할 수 있습니다. 이것은 우리에게 O (log n) 조회 시간을 제공합니다. 이것의 장점은 더 이상 큰 배열을 할당하지 않기 때문에 잠재적으로 더 적은 공간을 사용한다는 것입니다. 때때로 유용 할 수있는 순서대로 키를 반복 할 수도 있습니다.


5

BST는 또한 O (logn) 시간에 "findPredecessor"및 "findSuccessor"작업 (다음으로 가장 작은 요소와 다음으로 가장 큰 요소를 찾기 위해)을 제공하며 이는 매우 편리한 작업 일 수도 있습니다. Hash Table은 그 시간 효율성을 제공 할 수 없습니다.


"findPredecessor"및 "findSuccessor"작업을 찾고 있다면 HashTable은 처음부터 데이터 구조에 대한 잘못된 선택입니다.
AKDesai

1

정렬 된 방식으로 데이터에 액세스하려면 정렬 된 목록이 해시 테이블과 병렬로 유지되어야합니다. 좋은 예는 .Net의 Dictionary입니다. ( http://msdn.microsoft.com/en-us/library/3fcwy8h6.aspx 참조 ).

이것은 삽입 속도가 느려지는 부작용이있을뿐만 아니라 b- 트리보다 더 많은 양의 메모리를 소비합니다.

또한 b- 트리가 정렬되어 있기 때문에 결과 범위를 찾거나 통합 또는 병합을 수행하는 것이 간단합니다.


1

또한 용도에 따라 다르며 Hash는 정확한 일치를 찾을 수 있습니다. 범위를 쿼리하려면 BST가 선택입니다. 많은 데이터 e1, e2, e3 ..... en이 있다고 가정합니다.

해시 테이블을 사용하면 일정한 시간에 모든 요소를 ​​찾을 수 있습니다.

e41보다 크고 e8보다 작은 범위 값을 찾으려면 BST가 신속하게 찾을 수 있습니다.

핵심은 충돌을 피하기 위해 사용되는 해시 함수입니다. 물론 충돌을 완전히 피할 수는 없습니다.이 경우 연결이나 다른 방법을 사용합니다. 이로 인해 최악의 경우 검색이 더 이상 일정하지 않습니다.

일단 가득 차면 해시 테이블은 버킷 크기를 늘리고 모든 요소를 ​​다시 복사해야합니다. 이것은 BST에 존재하지 않는 추가 비용입니다.


1

해시 테이블은 인덱싱에 적합하지 않습니다. 범위를 검색 할 때 BST가 더 좋습니다. 이것이 대부분의 데이터베이스 인덱스가 해시 테이블 대신 B + 트리를 사용하는 이유입니다.


데이터베이스 인덱스는 해시 및 B + 트리 유형입니다. 보다 큼 또는보다 작음과 같은 비교를 수행하려면 B + 트리 인덱스가 유용하고 그렇지 않으면 해시 인덱스가 조회에 유용합니다. 또한 데이터가 비교할 수없는 경우를 생각하고 인덱스를 만들고 싶다면 db는 B + 트리 인덱스가 아닌 해시 인덱스를 생성합니다. @ssD
Sukhmeet 싱

1

이진 검색 트리는 키에 일부 총 순서 (키가 비교 가능)가 정의되어 있고 순서 정보를 보존하려는 경우 사전을 구현하는 데 좋은 선택입니다.

BST는 주문 정보를 보존하므로 해시 테이블을 사용하여 (효율적으로) 수행 할 수없는 4 개의 추가 동적 집합 작업을 제공합니다. 이러한 작업은 다음과 같습니다.

  1. 최고
  2. 최저한의
  3. 후임
  4. 전임자

모든 BST 작업과 마찬가지로 이러한 모든 작업은 O (H)의 시간 복잡도를 갖습니다. 또한 저장된 모든 키는 BST에서 정렬 된 상태로 유지되므로 트리를 순서대로 순회하여 정렬 된 키 시퀀스를 얻을 수 있습니다.

요약하면, 삽입, 삭제 및 제거 작업이 필요한 경우 해시 테이블은 성능면에서 타의 추종을 불허합니다 (대부분의 경우). 그러나 위에 나열된 작업 중 일부 또는 전부를 원할 경우 BST, 가급적 자체 균형 조정 BST를 사용해야합니다.


0

해시 테이블의 주요 장점은 ~ = O (1)에서 거의 모든 작업을 수행한다는 것입니다. 그리고 이해하고 구현하기가 매우 쉽습니다. 많은 "면접 문제"를 효율적으로 해결합니다. 따라서 코딩 인터뷰를 깨고 싶다면 해시 테이블로 가장 친한 친구를 사귀십시오 ;-)


OP는 해싱보다 BST의 장점을 요구했다고 생각합니다.
Sniper

0

해시 맵은 집합 연관 배열입니다. 따라서 입력 값 배열이 버킷으로 풀링됩니다. 개방형 주소 지정 체계에서는 버킷에 대한 포인터가 있으며 버킷에 새 값을 추가 할 때마다 버킷에서 여유 공간이있는 위치를 찾습니다. 이를 수행하는 몇 가지 방법이 있습니다. 버킷의 시작 부분에서 시작하여 매번 포인터를 증가시키고 점유 여부를 테스트합니다. 이를 선형 프로빙이라고합니다. 그런 다음 add와 같은 이진 검색을 수행 할 수 있습니다. 여기서 버킷의 시작 부분과 여유 공간을 검색 할 때마다 두 배로 늘리거나 뒤로 내리는 위치의 차이를 두 배로 늘릴 수 있습니다. 이것을 2 차 프로빙이라고합니다. 확인. 이제이 두 가지 방법의 문제는 버킷이 다음 버킷 주소로 오버플로되면 다음을 수행해야한다는 것입니다.

  1. 각 버킷 크기 두 배-malloc (N 버킷) / 해시 함수 변경-필요한 시간 : malloc 구현에 따라 다름
  2. 이전 버킷 데이터를 각각 새 버킷 데이터로 전송 / 복사합니다. 이것은 N이 전체 데이터를 나타내는 O (N) 연산입니다.

확인. 하지만 링크드리스트를 사용한다면 그런 문제가 안 되겠죠? 예, 연결된 목록에서는이 문제가 없습니다. 각 버킷이 연결 목록으로 시작하는 것을 고려하고, 버킷에 100 개의 요소가있는 경우 연결 목록의 끝에 도달하기 위해 해당 100 개 요소를 트래버스해야하므로 List.add (Element E)에 시간이 걸립니다.

  1. 요소를 버킷에 해시-모든 구현에서와 같이 Normal
  2. 상기 버킷 O (N) 연산에서 마지막 요소를 찾는 데 시간을 할애하십시오.

링크드리스트 구현의 장점은 오픈 주소 지정 구현의 경우와 같이 모든 버킷의 메모리 할당 작업 및 O (N) 전송 / 복사가 필요하지 않다는 것입니다.

따라서 O (N) 작업을 최소화하는 방법은 구현을 찾기 작업이 O (log (N)) 인 이진 검색 트리의 구현으로 변환하고 값에 따라 해당 위치에 요소를 추가하는 것입니다. BST의 추가 기능은 정렬되어 제공된다는 것입니다!


0

이진 검색 트리는 문자열 키와 함께 사용할 때 더 빠를 수 있습니다. 특히 문자열이 길 때.

문자열에 대해 빠른 비교를 사용하는 이진 검색 트리 (같지 않을 때). 따라서 BST는 문자열을 찾을 수 없을 때 신속하게 응답 할 수 있습니다. 발견되면 하나의 전체 비교 만 수행하면됩니다.

해시 테이블에서. 문자열의 해시를 계산해야하므로 해시를 계산하려면 모든 바이트를 한 번 이상 통과해야합니다. 그런 다음 다시 일치하는 항목이 발견되면.

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