해시 테이블에 비해 이진 검색 트리의 장점은 무엇입니까?
해시 테이블은 Theta (1) 시간의 모든 요소를 조회 할 수 있으며 요소를 추가하는 것만 큼 쉽습니다 ....하지만 그 반대 방향으로가는 이점이 확실하지 않습니다.
해시 테이블에 비해 이진 검색 트리의 장점은 무엇입니까?
해시 테이블은 Theta (1) 시간의 모든 요소를 조회 할 수 있으며 요소를 추가하는 것만 큼 쉽습니다 ....하지만 그 반대 방향으로가는 이점이 확실하지 않습니다.
답변:
이진 검색 트리 (참조 기반)는 메모리 효율적입니다. 필요한 것보다 더 많은 메모리를 예약하지 않습니다.
예를 들어 해시 함수에 범위가있는 R(h) = 0...100
경우 요소 20 개를 해싱하는 경우에도 100 (포인터) 요소의 배열을 할당해야합니다. 이진 검색 트리를 사용하여 동일한 정보를 저장하는 경우 필요한만큼의 공간과 링크에 대한 일부 메타 데이터 만 할당합니다.
다른 누구도 지적하지 않은 한 가지 장점은 이진 검색 트리를 사용하여 범위 검색을 효율적으로 수행 할 수 있다는 것입니다.
내 생각을 설명하기 위해 극단적 인 사례를 만들고 싶습니다. 키가 0에서 5000 사이 인 모든 요소를 가져오고 싶다고 가정 해 보겠습니다. 실제로 그러한 요소는 하나만 있고 키가 범위에없는 10000 개의 다른 요소가 있습니다. BST는 답을 얻을 수없는 하위 트리를 검색하지 않기 때문에 범위 검색을 매우 효율적으로 수행 할 수 있습니다.
그러나 해시 테이블에서 범위 검색을 어떻게 수행 할 수 있습니까? O (n) 인 모든 버킷 공간을 반복해야하거나 1,2,3,4 ... 개가 최대 5000 개까지 존재하는지 찾아야합니다. (0에서 5000 사이의 키는 무한 세트 인 것은 어떻습니까? 예를 들어 키는 십진수 일 수 있습니다)
이진 트리의 한 가지 "장점"은 모든 요소를 순서대로 나열하기 위해 순회 할 수 있다는 것입니다. 이것은 해시 테이블로는 불가능하지 않지만 해시 구조로 설계하는 정상적인 작업은 아닙니다.
다른 모든 좋은 댓글 외에도 :
일반적으로 해시 테이블은 이진 트리에 비해 적은 메모리 읽기가 필요한 캐시 동작이 더 좋습니다. 해시 테이블의 경우 일반적으로 데이터를 보유하는 참조에 액세스하기 전에 단일 읽기만 발생합니다. 이진 트리가 균형 잡힌 변형이라면 어떤 상수 k에 대해 k * lg (n) 메모리 읽기 순서의 무언가가 필요합니다 .
반면에 적이 당신의 해시 함수를 알고 있다면 적이 당신의 해시 테이블을 강제로 충돌을 일으키고 성능을 크게 저하시킬 수 있습니다. 해결 방법은 패밀리에서 임의로 해시 함수를 선택하는 것이지만 BST에는 이러한 단점이 없습니다. 또한 해시 테이블 압력이 너무 커지면 비용이 많이 드는 작업이 될 수있는 해시 테이블을 확대하고 재 할당하는 경향이 있습니다. BST는 여기에서 더 간단한 동작을 가지며 갑자기 많은 데이터를 할당하고 재해 싱 작업을 수행하지 않는 경향이 있습니다.
트리는 궁극적 인 평균 데이터 구조 인 경향이 있습니다. 목록 역할을 할 수 있고 병렬 작업을 위해 쉽게 분할 할 수 있으며 O (lg n) 순서로 빠른 제거, 삽입 및 조회가 가능합니다 . 그들은 특별히 잘하지 않지만 지나치게 나쁜 행동도 없습니다.
마지막으로, BST는 해시 테이블에 비해 (순수한) 기능적 언어로 구현하기가 훨씬 쉽고 파괴적인 업데이트를 구현할 필요가 없습니다 ( 위의 Pascal 의 지속성 인수).
BSTs are much easier to implement in (pure) functional languages compared to hash-tables
- 정말? 나는 지금 기능적 언어를 배우고 싶다!
해시 테이블에 비해 이진 트리의 주요 장점은 이진 트리가 해시 테이블로 (쉽고 빠르게) 수행 할 수없는 두 가지 추가 작업을 제공한다는 것입니다.
임의의 키 값 (또는 가장 가까운 위 / 아래)에 가장 가까운 (반드시 같지는 않음) 요소 찾기
정렬 된 순서로 트리의 내용을 반복합니다.
둘은 연결되어 있습니다. 이진 트리는 내용을 정렬 된 순서로 유지하므로 정렬 된 순서가 필요한 작업을 쉽게 수행 할 수 있습니다.
(균형) 이진 검색 트리는 점근 적 복잡성이 실제로 상한이라는 장점이 있지만 해시 테이블의 "상수"시간은 상각 된 시간입니다. 부적합한 해시 함수가 있으면 결국 선형 시간으로 저하 될 수 있습니다. , 일정하지 않습니다.
해시 테이블은 처음 생성 될 때 더 많은 공간을 차지합니다.-아직 삽입되지 않은 요소 (삽입 여부에 관계없이)에 대해 사용 가능한 슬롯이 있으며 이진 검색 트리는 필요한만큼만 커집니다. 있다. 해시 테이블이 더 많은 공간을 필요로 할 때 또 다른 구조로 확장하는 것은 수 많은 시간이 소요될 수 있지만 그 구현에 달려 있습니다.
이진 트리는 검색 및 삽입 속도가 더 느리지 만 중위 순회의 매우 멋진 기능이 있습니다. 이는 기본적으로 트리의 노드를 정렬 된 순서로 반복 할 수 있음을 의미합니다.
해시 테이블의 항목을 반복하는 것은 모두 메모리에 흩어져 있기 때문에 별 의미가 없습니다.
BST (균형 이진 검색 트리)를 사용하여 해시 테이블을 구현할 수 있습니다. 이것은 우리에게 O (log n) 조회 시간을 제공합니다. 이것의 장점은 더 이상 큰 배열을 할당하지 않기 때문에 잠재적으로 더 적은 공간을 사용한다는 것입니다. 때때로 유용 할 수있는 순서대로 키를 반복 할 수도 있습니다.
정렬 된 방식으로 데이터에 액세스하려면 정렬 된 목록이 해시 테이블과 병렬로 유지되어야합니다. 좋은 예는 .Net의 Dictionary입니다. ( http://msdn.microsoft.com/en-us/library/3fcwy8h6.aspx 참조 ).
이것은 삽입 속도가 느려지는 부작용이있을뿐만 아니라 b- 트리보다 더 많은 양의 메모리를 소비합니다.
또한 b- 트리가 정렬되어 있기 때문에 결과 범위를 찾거나 통합 또는 병합을 수행하는 것이 간단합니다.
또한 용도에 따라 다르며 Hash는 정확한 일치를 찾을 수 있습니다. 범위를 쿼리하려면 BST가 선택입니다. 많은 데이터 e1, e2, e3 ..... en이 있다고 가정합니다.
해시 테이블을 사용하면 일정한 시간에 모든 요소를 찾을 수 있습니다.
e41보다 크고 e8보다 작은 범위 값을 찾으려면 BST가 신속하게 찾을 수 있습니다.
핵심은 충돌을 피하기 위해 사용되는 해시 함수입니다. 물론 충돌을 완전히 피할 수는 없습니다.이 경우 연결이나 다른 방법을 사용합니다. 이로 인해 최악의 경우 검색이 더 이상 일정하지 않습니다.
일단 가득 차면 해시 테이블은 버킷 크기를 늘리고 모든 요소를 다시 복사해야합니다. 이것은 BST에 존재하지 않는 추가 비용입니다.
해시 테이블은 인덱싱에 적합하지 않습니다. 범위를 검색 할 때 BST가 더 좋습니다. 이것이 대부분의 데이터베이스 인덱스가 해시 테이블 대신 B + 트리를 사용하는 이유입니다.
이진 검색 트리는 키에 일부 총 순서 (키가 비교 가능)가 정의되어 있고 순서 정보를 보존하려는 경우 사전을 구현하는 데 좋은 선택입니다.
BST는 주문 정보를 보존하므로 해시 테이블을 사용하여 (효율적으로) 수행 할 수없는 4 개의 추가 동적 집합 작업을 제공합니다. 이러한 작업은 다음과 같습니다.
모든 BST 작업과 마찬가지로 이러한 모든 작업은 O (H)의 시간 복잡도를 갖습니다. 또한 저장된 모든 키는 BST에서 정렬 된 상태로 유지되므로 트리를 순서대로 순회하여 정렬 된 키 시퀀스를 얻을 수 있습니다.
요약하면, 삽입, 삭제 및 제거 작업이 필요한 경우 해시 테이블은 성능면에서 타의 추종을 불허합니다 (대부분의 경우). 그러나 위에 나열된 작업 중 일부 또는 전부를 원할 경우 BST, 가급적 자체 균형 조정 BST를 사용해야합니다.
해시 맵은 집합 연관 배열입니다. 따라서 입력 값 배열이 버킷으로 풀링됩니다. 개방형 주소 지정 체계에서는 버킷에 대한 포인터가 있으며 버킷에 새 값을 추가 할 때마다 버킷에서 여유 공간이있는 위치를 찾습니다. 이를 수행하는 몇 가지 방법이 있습니다. 버킷의 시작 부분에서 시작하여 매번 포인터를 증가시키고 점유 여부를 테스트합니다. 이를 선형 프로빙이라고합니다. 그런 다음 add와 같은 이진 검색을 수행 할 수 있습니다. 여기서 버킷의 시작 부분과 여유 공간을 검색 할 때마다 두 배로 늘리거나 뒤로 내리는 위치의 차이를 두 배로 늘릴 수 있습니다. 이것을 2 차 프로빙이라고합니다. 확인. 이제이 두 가지 방법의 문제는 버킷이 다음 버킷 주소로 오버플로되면 다음을 수행해야한다는 것입니다.
확인. 하지만 링크드리스트를 사용한다면 그런 문제가 안 되겠죠? 예, 연결된 목록에서는이 문제가 없습니다. 각 버킷이 연결 목록으로 시작하는 것을 고려하고, 버킷에 100 개의 요소가있는 경우 연결 목록의 끝에 도달하기 위해 해당 100 개 요소를 트래버스해야하므로 List.add (Element E)에 시간이 걸립니다.
링크드리스트 구현의 장점은 오픈 주소 지정 구현의 경우와 같이 모든 버킷의 메모리 할당 작업 및 O (N) 전송 / 복사가 필요하지 않다는 것입니다.
따라서 O (N) 작업을 최소화하는 방법은 구현을 찾기 작업이 O (log (N)) 인 이진 검색 트리의 구현으로 변환하고 값에 따라 해당 위치에 요소를 추가하는 것입니다. BST의 추가 기능은 정렬되어 제공된다는 것입니다!