레드-블랙 나무가 왜 그렇게 인기가 있습니까?


46

내가 보는 모든 곳에서 데이터 구조는 레드 블랙 트리 ( std::setC ++, SortedDictionaryC # 등)를 사용하여 구현되고있는 것 같습니다 .

알고리즘 클래스에서 방금 (a, b), 빨강-검정 및 AVL 트리를 다루었으므로 여기에 나와 있습니다 (교수들에게 물어보고 몇 권의 책을보고 약간의 인터넷 검색을 통해).

  • AVL 트리는 빨강 검정 트리보다 평균 깊이가 작으므로 AVL 트리에서 값을 검색하는 것이 일관되게 빠릅니다.
  • 레드-블랙 트리는 AVL 트리보다 균형을 잡기 위해 구조적인 변화가 적으므로 삽입 / 삭제 속도가 더 빠릅니다. 나는 잠재적으로 말하고있다. 나무에 대한 구조적 변화 비용에 달려 있기 때문에 런타임과 구현에 많이 의존하기 때문에 (나무가 불변 할 때 기능적 언어에서 완전히 다를 수 있습니까?)

온라인으로 AVL과 Red-black 트리를 비교하는 벤치 마크가 많이 있지만, 제게 놀란 것은 교수님이 기본적으로 두 가지 중 하나를 수행한다는 것입니다.

  • 성능에 대해서는 그다지 신경 쓰지 않아도됩니다.이 경우 대부분의 경우 AVL과 Red-black의 10-20 % 차이는 전혀 중요하지 않습니다.
  • 또는 실제로 성능에 관심이 있습니다.이 경우 AVL과 Red-black 트리를 모두 버리고 B 트리와 함께 사용하면 훨씬 더 잘 작동하도록 조정할 수 있습니다 (또는 (a, b) 트리, 나는 ' 한 바구니에 모두 넣을 것입니다.)

그 이유는 B- 트리가 데이터를 메모리에보다 콤팩트하게 저장하기 때문에 (한 노드에는 많은 값이 포함됨) 캐시 누락이 훨씬 적기 때문입니다. 유스 케이스를 기반으로 구현을 조정하고 B- 트리의 순서를 CPU 캐시 크기 등에 의존하게 만들 수도 있습니다.

문제는 실제 현대 하드웨어에서 다양한 검색 트리 구현의 실제 사용량을 분석하는 거의 모든 소스를 찾을 수 없다는 것입니다. 알고리즘에 대한 많은 책을 살펴 보았고 다른 트리 변형을 비교할만한 것을 찾지 못했습니다. 다른 트리 변형을 비교할 수는 없습니다. 실제 프로그램에서)

즉, 위에서 언급 한 것을 바탕으로 B- 트리가 성능을 능가 해야하는 이유가 무엇이든 레드 블랙 트리가 모든 곳에서 사용되는 특별한 이유가 있습니까? (내가 찾을 수있는 유일한 벤치 마크로 http://lh3lh3.users.sourceforge.net/udb.shtml 도 표시 되지만 특정 구현의 문제 일 수 있습니다). 아니면 모든 사람이 레드 블랙 트리를 사용하는 이유는 구현하기가 어렵거나 구현하기가 어렵 기 때문에 다른 단어로 표현하기 때문입니까?

또한 기능 언어의 영역으로 이동할 때 어떻게 변경됩니까? Clojure와 Scala는 모두 해시 배열 매핑 된 시도를 사용하는 것으로 보입니다 . Clojure는 분기 계수 32를 사용합니다.


8
고통을 가중시키기 위해 여러 종류의 검색 트리를 비교하는 대부분의 기사는 이상적인 실험보다 성능이 떨어집니다.
Raphael

1
내 생각에 AVL 트리는 레드 블랙 트리보다 쉽게 ​​구현 할 수 있으며 (밸런싱이 적은 경우) 성능에서 큰 차이를 느끼지 못했습니다.
Jordi Vermeulen

3
stackoverflow에서 친구들의 관련 토론 왜 std :: map이 빨강-검정 트리로 구현됩니까? .
Hendrik 1

답변:


10

으로부터 인용 대답 "에 AVL 나무와 레드 블랙 트리의 루트에서 순회 질문"

AVL 트리가 아닌 레드-블랙 트리를 포함한 일부 이진 검색 트리의 경우, 트리에 대한 "수정"은 다운 다운 과정에서 쉽게 예측하고 수행 할 수 있으므로 두 번째 패스는 불필요합니다. 이러한 삽입 알고리즘은 일반적으로 재귀가 아닌 루프로 구현되며 실제로는 2 패스 대응보다 약간 더 빠르게 실행됩니다.

따라서 RedBlack 트리 삽입은 재귀없이 구현 될 수 있습니다. 일부 CPU 에서는 함수 호출 캐시를 오버런하면 재귀가 매우 비쌉니다 (예 : 등록 창 사용으로 인한 SPARC )

(한 번의 함수 호출을 제거하여 Sparc에서 소프트웨어가 10 배 이상 빠르게 실행되는 것을 보았습니다. 이로 인해 종종 호출되는 코드 경로가 레지스터 창에 대해 너무 깊습니다. 레지스터 창이 얼마나 깊을 지 알 수 없습니다. 고객의 시스템과 재귀를 사용하지 않고 "핫 코드 경로"에있는 콜 스택이 얼마나 멀리 있는지 알지 못합니다.)

또한 스택이 부족할 위험이없는 것이 장점입니다.


그러나 2 ^ 32 노드가있는 균형 잡힌 트리는 약 32 수준의 재귀를 요구하지 않습니다. 스택 프레임이 64 바이트라도 스택 공간이 2KB를 넘지 않습니다. 실제로 차이를 만들 수 있습니까? 나는 그것을 의심 할 것이다.
Björn Lindqvist

@ BjörnLindqvist, 1990 년대의 SPARC 프로세서에서 공통 코드 경로를 스택 깊이 7에서 6으로 변경하여 10 배 이상의 속도를 얻었습니다. 파일 등록 방법을 읽어보십시오.
Ian Ringrose

9

나는 최근 에이 주제를 연구 해 왔으므로 여기에 내가 찾은 결과가 있지만 데이터 구조 전문가가 아님을 명심하십시오!

B- 트리를 전혀 사용할 수없는 경우가 있습니다.

대표적인 사례 중 하나는 std::mapC ++ STL입니다. 표준은 insert기존 반복자를 무효화하지 않아야합니다.

무효화 된 반복 자나 참조가 없습니다.

http://en.cppreference.com/w/cpp/container/map/insert

삽입은 기존 요소 주위를 이동하므로 B- 트리를 구현으로 제외합니다.

또 다른 유사한 유스 케이스는 침입 데이터 구조입니다. 즉, 데이터를 트리의 노드에 저장하는 대신 구조체 내부의 자식 / 부모에 대한 포인터를 저장합니다.

// non intrusive
struct Node<T> {
    T value;
    Node<T> *left;
    Node<T> *right;
};
using WalrusList = Node<Walrus>;

// intrusive
struct Walrus {
    // Tree part
    Walrus *left;
    Walrus *right;

    // Object part
    int age;
    Food[4] stomach;
};

B- 트리는 포인터 전용 데이터 구조가 아니기 때문에 침입 성을 만들 수 없습니다.

예를 들어, jemalloc 에서 사용 가능한 메모리 블록을 관리 하는 데 방해가되는 레드-블랙 트리가 사용됩니다 . 이것은 Linux 커널에서 널리 사용되는 데이터 구조이기도합니다.

또한 "단일 패스 테일 재귀"구현이 변경 가능한 데이터 구조 로서 레드 블랙 트리 인기의 이유 가 아니라고 생각합니다 .

로그

영형(1)

영형(1)

opendatastructures에 설명 된 변형은 부모 포인터, 삽입을위한 재귀 다운 패스 및 수정을위한 반복 루프 업 패스를 사용합니다. 재귀 호출은 꼬리 위치에 있으며 컴파일러는 이것을 루프로 최적화합니다 (Russt에서 이것을 확인했습니다).

영형(1)


3

글쎄, 이것은 권위있는 대답은 아니지만 균형 잡힌 이진 검색 트리를 코딩해야 할 때마다 빨강-검은 나무입니다. 이에 대한 몇 가지 이유가 있습니다.

1) 평균 삽입 비용은 레드 블랙 트리의 경우 (검색 할 필요가없는 경우) 일정하며 AVL 트리의 경우 로그입니다. 또한 최대 하나의 복잡한 구조 조정이 필요합니다. 최악의 경우 여전히 O (log N)이지만 간단한 재 색상입니다.

2) 노드 당 1 비트의 추가 정보 만 필요하며 무료로 얻을 수있는 방법을 종종 찾을 수 있습니다.

3) 나는 이것을 매우 자주 할 필요가 없으므로 그것을 할 때마다 다시하는 방법을 알아 내야합니다. 간단한 규칙과 2-4 개의 나무와의 대응 은 코드가 매번 복잡 해지더라도 매번 쉬워 보인다 . 나는 여전히 언젠가 코드가 간단 해지기를 바랍니다.

4) 레드 블랙 트리가 해당 2-4 트리 노드를 분할하고 다시 칠하는 것만 으로 중간 키를 부모 2-4 노드에 삽입하는 방법 은 매우 우아합니다. 나는 단지 그것을 좋아합니다.


0

레드-블랙 또는 AVL 트리는 키가 길거나 다른 이유로 키를 이동하는 것이 비싸면 B- 트리 등에 비해 이점이 있습니다.

std::set여러 가지 성능상의 이유로 주요 프로젝트 내에서 나만의 대안을 만들었습니다 . 성능상의 이유로 레드-블랙보다 AVL을 선택했지만 작은 성능 향상은 std :: set 대신 내 자신의 롤링에 대한 정당성이 아닙니다. 복잡하고 움직이기 어려운 "키"는 중요한 요소였습니다. 키 앞에 다른 수준의 간접 지시가 필요한 경우 (a, b) 나무가 여전히 의미가 있습니까? AVL 및 빨강 검정 트리는 키를 이동하지 않고도 재구성 할 수 있으므로 키를 이동하는 데 비용이 많이 드는 경우 이점이 있습니다.


아이러니하게도, 적-검은 나무는 (a, b)-나무의 특수한 경우 일 뿐이므로 문제가 매개 변수를 조정하는 것으로 보입니까? (cc @Gilles)
Raphael
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.