왜 파이썬은 해시 테이블을 사용하여 dict를 구현하지만 Red-Black Tree는 구현하지 않습니까? [닫은]


11

왜 파이썬은 해시 테이블을 사용하여 dict를 구현하지만 Red-Black Tree는 구현하지 않습니까?

열쇠는 무엇입니까? 공연?


2
귀하의 연구를 공유하면 모든 사람을 도울 수 있습니다. 당신이 무엇을 시도했고 왜 그것이 당신의 요구를 충족시키지 못했는지 알려주십시오. 이것은 당신이 시간을내어 자신을 돕기 위해 노력했고, 명백한 답변을 반복하지 못하게하며, 무엇보다도보다 구체적이고 관련성있는 답변을 얻는 데 도움이됩니다. 또한 물어
gnat

답변:


16

이것은 파이썬이 아닌 일반적인 답변입니다.

알고리즘 복잡도 비교

       | Hash Table  |   Red-Black Tree    |
-------+-------------+---------------------+
Space  | O(n) : O(n) | O(n)     : O(n)     |
Insert | O(1) : O(n) | O(log n) : O(log n) |
Fetch  | O(1) : O(n) | O(log n) : O(log n) |
Delete | O(1) : O(n) | O(log n) : O(log n) |
       | avg  :worst | average  : worst    |

해시 테이블의 문제점은 해시가 충돌 할 수 있다는 것입니다. 충돌을 해결하기위한 다양한 메커니즘, 예를 들어 개방형 주소 지정 또는 별도의 체인 연결이 있습니다. 최악의 경우는 모든 키에 동일한 해시 코드가 있으며,이 경우 해시 테이블이 연결된 목록으로 저하됩니다.

다른 모든 경우에 해시 테이블은 구현하기 쉽고 우수한 성능을 제공하는 훌륭한 데이터 구조입니다. 단점은 빠르게 테이블을 늘리고 항목을 재분배 할 수있는 구현은 실제로 사용되는 메모리를 거의 낭비 할 수 있다는 것입니다.

RB- 트리는 자체 균형을 유지하며 최악의 경우 알고리즘 복잡성을 변경하지 않습니다. 그러나 구현하기가 더 어렵습니다. 평균 복잡도는 해시 테이블보다 복잡합니다.

키 제한

해시 테이블의 모든 키는 서로 해시 가능하고 비교 가능해야합니다. 이것은 문자열이나 정수에 특히 쉽지만 사용자 정의 유형으로 확장하는 것도 매우 간단합니다. Java와 같은 일부 언어에서는 이러한 속성이 정의에 의해 보장됩니다.

RB-Tree의 키는 총 순서를 가져야합니다. 각 키는 다른 키와 비교 가능해야하며 두 키는 더 작거나 크거나 같아야합니다. 이 순서 평등은 시맨틱 평등과 동일해야합니다. 이것은 정수 및 기타 숫자에 대해 간단하며 문자열에도 상당히 쉽습니다 (순서는 일관성 있고 외부에서 관찰 할 수 없으므로 순서는 로케일을 고려할 필요가 없음 [1] ). . 서로 다른 유형의 키를 갖는 것은 불가능합니다.

[1] : 사실, 여기가 잘못되었습니다. 두 문자열은 바이트가 같지 않지만 일부 언어의 규칙에 따라 동일 할 수 있습니다. 두 개의 동일한 문자열이 다르게 인코딩되는 예는 유니 코드 정규화를 참조하십시오. 해시 키에 유니 코드 문자 구성이 중요한지 여부는 해시 테이블 구현에서 알 수없는 것입니다.

RB-Tree 키를위한 저렴한 솔루션은 먼저 동등성을 테스트 한 다음 동일성을 비교 (즉, 포인터 비교)하는 것이라고 생각할 수 있습니다. 그러나이 순서는 전 이적이지 않습니다. if a == b및 이면 순서 id(a) > id(c)를 따라야합니다 id(b) > id(c). 여기에서는 보장되지 않습니다. 대신, 키의 해시 코드를 조회 키로 사용할 수 있습니다. 여기서 순서는 올바르게 작동하지만 RB 트리에서 동일한 노드에 할당 될 동일한 해시 코드를 가진 여러 개의 고유 키로 끝날 수 있습니다. 이러한 해시 충돌을 해결하기 위해 우리는 해시 테이블과 마찬가지로 별도의 체인을 사용할 수 있지만 해시 테이블에 대한 최악의 동작을 상속합니다.

다른 양상들

  • 해시 테이블은 본질적으로 단지 배열이기 때문에 해시 테이블이 트리보다 더 나은 메모리 위치를 가질 것으로 기대합니다.

  • 두 데이터 구조의 항목은 상당히 높은 오버 헤드를 갖습니다.

    • 해시 테이블 : 개별 체인의 경우 키, 값 및 다음 항목 포인터 또한 해시 코드를 저장하면 크기를 빠르게 조정할 수 있습니다.
    • RB- 트리 : 키, 값, 색상, 왼쪽 자식 포인터, 오른쪽 자식 포인터. 색상이 단일 비트이지만 정렬 문제로 인해 전체 포인터 또는 2 크기의 메모리 블록 만 할당 할 수있는 경우 거의 4 개의 포인터에 충분한 공간을 낭비하고있을 수 있습니다. 어쨌든 RB- 트리 항목은 해시 테이블 항목보다 더 많은 메모리를 소비합니다.
  • RB- 트리에서 삽입 및 삭제에는 트리 회전이 포함됩니다. 비용이 많이 들지는 않지만 오버 헤드가 발생합니다. 해시에서 삽입 및 삭제는 단순한 액세스보다 비용이 많이 들지 않습니다 (삽입시 해시 테이블의 크기를 조정하는 것은 O(n)노력 이지만 ).

  • 해시 테이블은 본질적으로 변경 가능하지만 RB- 트리는 변경 불가능한 방식으로 구현 될 수도 있습니다. 그러나 이것은 거의 유용하지 않습니다.


해시 충돌을위한 작은 RB- 트리가있는 해시 테이블을 가질 수 있습니까?
aragaer

@aragaer는 일반적으로 아니지만 특정 경우에 가능합니다. 그러나 충돌은 일반적으로 링크 된 목록으로 처리됩니다. 일반적으로 충돌이 거의 없기 때문에 구현하기가 훨씬 쉽고 오버 헤드가 적으며 성능이 훨씬 뛰어납니다. 충돌이 많이 예상되면 해시 함수를 변경하거나 더 간단한 B- 트리를 사용할 수 있습니다. RB 트리와 같은 자체 균형 트리는 훌륭하지만 단순히 가치를 추가하지 않는 경우가 많습니다.
amon

나무에는 "<"를 지원하는 객체가 필요합니다. 해시 테이블에는 해시 + "="를 지원하는 개체가 필요합니다. 따라서 RB 트리가 불가능할 수 있습니다. 그러나 실제로 해시 테이블에 상당한 양의 충돌이 있으면 키 충돌을위한 대체 알고리즘이 아닌 새로운 해시 함수가 필요합니다.
gnasher729

1

여러 가지 이유가 있을 수 있지만 주요 이유는 다음과 같습니다.

  • 해시 테이블은 나무보다 구현하기 쉽습니다. 둘 다 완전히 사소한 것은 아니지만 해시 테이블이 조금 더 쉬우 며 해싱 함수와 평등 함수가 필요하기 때문에 법적 키 도메인에 미치는 영향은 덜 엄격합니다. 나무에는 총 주문 기능이 필요하므로 작성하기가 훨씬 어렵습니다.
  • 해시 테이블은 작은 크기에서 더 나은 성능을 발휘할 수 있습니다. 이론적으로는 상당 부분의 작업이 대규모 데이터 세트 만 다루기 때문에 이는 매우 중요합니다. 실제로, 실제로는 수백만이 아닌 수십 또는 수백 개의 키로 실제로 작동합니다. 소규모 성능은 매우 중요하며 점근 분석을 사용하여 가장 적합한 것을 파악할 수 없습니다. 실제로 구현하고 측정해야합니다.

쓰기 / 유지가 쉬우 며 일반적인 사용 사례에서 성능이 향상됩니까? 가입하세요!

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