답변:
이진 트리 의 성능에 대한 논쟁 은 의미가 없습니다. 데이터 구조가 아니라 성능 특성이 다른 데이터 구조 패밀리입니다. 이 것은 사실이지만 불균형 이진 나무 보다 훨씬 더 수행 자체 균형 이진 트리 검색을위한 많은 이진 트리가 있습니다 (예 : 바이너리 시도 등) 하는 "균형" 아무 의미가 없습니다.
map
set
이진 트리가 검색을 위해 n-ary 트리보다 자주 사용되는 이유는 n-ary 트리가 더 복잡하지만 일반적으로 실제 속도 이점이 없기 때문입니다.
m
노드가 있는 (균형) 이진 트리 에서 한 레벨에서 다음 레벨로 이동하려면 한 번의 비교가 필요 log_2(m)
하며 총 log_2(m)
비교를 위해 레벨 이 있습니다 .
반대로, n-ary 트리는 이진 검색 을 사용하여 다음 레벨로 이동하기 위해 log_2(n)
비교 가 필요 합니다. 이 때문에 총 수준의 검색이 필요합니다 = 비교는 총. 따라서 n-ary 트리는 더 복잡하지만 필요한 총 비교 측면에서 이점이 없습니다.log_n(m)
log_2(n)*log_n(m)
log_2(m)
(단, n-ary 트리는 여전히 틈새 상황에 유용합니다. 즉시 떠오르는 예는 쿼드 트리 및 다른 공간 분할 트리입니다. 여기서 레벨 당 두 개의 노드 만 사용하여 공간을 분할하면 논리가 불필요하게 복잡해집니다. 제한 수준이 각 수준에서 수행되는 비교 횟수가 아니라 하드 드라이브에서 한 번에로드 할 수있는 노드 수인 많은 데이터베이스에서 사용되는 B- 트리 )
대부분의 사람들이 이진 트리에 대해 이야기 할 때, 이진 검색 트리 에 대해 생각하지 않는 것보다 더 흔하기 때문에 먼저 다룰 것입니다.
불균형 이진 검색 트리는 실제로 학생들에게 데이터 구조에 대한 교육 이상을 제공하는 데 유용합니다. 데이터가 비교적 임의의 순서로 나오지 않는 한 단순한 이진 트리의 균형 이 맞지 않기 때문에 트리가 최악의 형태로 쉽게 변질 될 수 있기 때문 입니다.
좋은 사례 : 한 번은 조작 및 검색을 위해 데이터를 이진 트리에로드 한 일부 소프트웨어를 수정해야했습니다. 데이터를 정렬 된 형식으로 작성했습니다.
Alice
Bob
Chloe
David
Edwina
Frank
다시 읽을 때 다음 트리로 끝났습니다.
Alice
/ \
= Bob
/ \
= Chloe
/ \
= David
/ \
= Edwina
/ \
= Frank
/ \
= =
이것은 퇴화 형태입니다. 해당 트리에서 Frank를 찾으면 6 개의 노드를 모두 검색해야 찾을 수 있습니다.
이진 트리는 균형을 잡을 때 검색에 정말 유용합니다. 여기에는 루트 노드를 통해 하위 트리를 회전하여 두 하위 트리 사이의 높이 차이가 1 이하가되도록합니다. 한 번에 하나씩 위의 이름을 균형 트리에 추가하면 다음과 같은 시퀀스가 제공됩니다.
1. Alice
/ \
= =
2. Alice
/ \
= Bob
/ \
= =
3. Bob
_/ \_
Alice Chloe
/ \ / \
= = = =
4. Bob
_/ \_
Alice Chloe
/ \ / \
= = = David
/ \
= =
5. Bob
____/ \____
Alice David
/ \ / \
= = Chloe Edwina
/ \ / \
= = = =
6. Chloe
___/ \___
Bob Edwina
/ \ / \
Alice = David Frank
/ \ / \ / \
= = = = = =
항목이 추가됨에 따라 전체 하위 트리가 왼쪽으로 회전하는 것을 볼 수 있습니다 (3 단계와 6 단계 O(log N)
에서 O(N
). 어느 시점에서도 가장 높은 NULL ( =
)은 둘 이상의 레벨만큼 가장 낮은 것과 다릅니다. 그리고, 위의 최종 트리에, 당신은 (세 개의 노드를보고 프랭크을 찾을 수 있습니다 Chloe
, Edwina
마지막으로하고 Frank
).
물론 이진 트레스가 아닌 균형 잡힌 멀티 웨이 트리 를 만들 때 더 유용 할 수 있습니다 . 이는 각 노드가 하나 이상의 항목을 보유 함을 의미합니다 (기술적으로 N 항목과 N + 1 포인터를 보유합니다. 이진 트리는 1 항목과 2 포인터가있는 단방향 다중 경로 트리의 특수한 경우입니다).
3 방향 트리를 사용하면 다음과 같이 끝납니다.
Alice Bob Chloe
/ | | \
= = = David Edwina Frank
/ | | \
= = = =
일반적으로 항목 인덱스의 키를 유지 관리하는 데 사용됩니다. 노드가 정확히 디스크 블록 크기 (예 : 512 바이트) 인 하드웨어에 최적화 된 데이터베이스 소프트웨어를 작성했으며 단일 노드에 가능한 많은 키를 넣었습니다. 포인터 이 경우 실제로 인덱스 파일에서 별도의 고정 길이 레코드 직접 액세스 파일에 기록 수 있었다 (그래서 레코드 번호는 X
단지에 추구로 볼 수 있습니다 X * record_length
).
예를 들어 포인터가 4 바이트이고 키 크기가 10 인 경우 512 바이트 노드의 키 수는 36입니다. 총 36 개의 키 (360 바이트)와 37 개의 포인터 (148 바이트)는 총 508 바이트입니다. 노드 당 4 바이트가 낭비됩니다.
다 방향 키를 사용하면 2 단계 검색 (노드에서 올바른 키를 찾기 위해 작은 순차적 (또는 선형 이진) 검색과 결합 된 올바른 노드를 찾기위한 다 방향 검색)의 복잡성이 발생하지만 장점은 다음과 같습니다. 이보다 더 적은 디스크 I / O를 수행합니다.
메모리 내 구조 에서이 작업을 수행 할 이유가 없습니다. 균형 이진 트리를 고수하고 코드를 간단하게 유지하는 것이 좋습니다.
또한 데이터 세트가 작을 때 O(log N)
오버 의 장점이 O(N)
실제로 나타나지는 않습니다. 주소록에 15 명을 저장하기 위해 다 방향 트리를 사용하는 경우 아마 과잉 일 수 있습니다. 지난 10 년 동안 수십만 고객의 모든 주문과 같은 것을 저장할 때 장점이 있습니다.
big-O 표기법의 요점은 N
무한대 에 접근 할 때 발생하는 상황을 나타내는 것 입니다. 어떤 사람들은 동의하지 않을 수도 있지만 데이터 세트가 특정 크기 이하로 유지되는 것이 확실하다면 다른 방법으로 거품 정렬을 사용하는 것이 좋습니다. :-)
이진 트리의 다른 용도와 관련하여 다음과 같은 많은 것들이 있습니다.
검색 트리에 대해 생성 한 설명이 많으면 다른 것에 대해 자세하게 설명하고 싶지만 원하는 경우 조사하기에 충분해야합니다.
이진 트리는 각 노드에 최대 두 개의 자식 노드가 있으며 일반적으로 "왼쪽"과 "오른쪽"으로 구분되는 트리 데이터 구조입니다. 자식이있는 노드는 부모 노드이며 자식 노드는 부모에 대한 참조를 포함 할 수 있습니다. 트리 외부에는 "루트"노드 (모든 노드의 조상)가있는 경우 종종 참조가 있습니다. 데이터 구조의 모든 노드는 루트 노드에서 시작하여 왼쪽 또는 오른쪽 자식에 대한 참조를 반복하여 도달 할 수 있습니다. 이진 트리에서 모든 노드의 정도는 최대 2입니다.
이진 트리는 그림에서 볼 수 있듯이 트리에서 노드를 찾으려면 최대 6 회만 보이기 때문에 유용합니다. 예를 들어 노드 24를 검색하려면 루트에서 시작합니다.
이 검색은 다음과 같습니다.
첫 번째 패스에서 전체 트리 노드의 절반을 제외 할 수 있음을 알 수 있습니다. 왼쪽 하위 트리의 절반은 두 번째입니다. 이것은 매우 효과적인 검색을 가능하게합니다. 이 작업이 40 억 개의 요소에서 수행 된 경우 최대 32 회만 검색하면됩니다. 따라서 트리에 포함 된 요소가 많을수록 검색 효율이 높아집니다.
삭제가 복잡해질 수 있습니다. 노드에 0 또는 1 개의 자식이있는 경우 삭제하려는 포인터를 제외하기 위해 일부 포인터를 이동하면됩니다. 그러나 자식이 2 개인 노드는 쉽게 삭제할 수 없습니다. 그래서 우리는 지름길을 취합니다. 노드 19를 삭제하려고한다고 가정하겠습니다.
왼쪽 및 오른쪽 포인터를 어디로 옮길 지 결정하기가 쉽지 않기 때문에 대체 포인터를 찾습니다. 왼쪽 하위 트리로 가서 가능한 한 오른쪽으로갑니다. 이것은 우리가 삭제할 노드의 다음으로 큰 값을 제공합니다.
이제 왼쪽 및 오른쪽 포인터를 제외한 18 개의 내용을 모두 복사하고 원래 18 개의 노드를 삭제합니다.
이러한 이미지를 만들기 위해 자체 균형 트리 인 AVL 트리를 구현하여 언제든지 트리가 리프 노드 (자식이없는 노드)간에 최대 한 수준의 차이를 갖도록합니다. 이렇게하면 O(log n)
삽입 및 삭제에 약간의 시간이 소요 되므로 트리가 기울어지지 않고 최대 검색 시간이 유지 됩니다.
다음은 내 AVL 트리가 가능한 작고 균형을 유지 한 방법을 보여주는 샘플입니다.
정렬 된 배열에서 조회는 여전히 O(log(n))
나무처럼 걸리지 만 무작위 삽입 및 제거에는 나무 대신 O (n)이 필요합니다 O(log(n))
. 일부 STL 컨테이너는 이러한 성능 특성을 활용하여 삽입 및 제거 시간이 최대로 걸리므로 O(log n)
매우 빠릅니다. 이러한 컨테이너 중 일부는 map
, multimap
, set
, 및 multiset
.
AVL 트리의 예제 코드는 http://ideone.com/MheW8 에서 찾을 수 있습니다.
주요 응용 프로그램은 이진 검색 트리 입니다. 검색, 삽입 및 제거가 모두 매우 빠른 데이터 구조입니다 ( log(n)
작업 에 대해 ).
언급되지 않은 이진 트리의 한 가지 흥미로운 예는 재귀 적으로 평가 된 수학적 표현입니다. 기본적으로 실용적인 관점에서 쓸모는 없지만 그러한 표현을 생각하는 흥미로운 방법입니다.
기본적으로 트리의 각 노드는 고유 한 값을 갖거나 하위 값을 조작하여 재귀 적으로 평가됩니다.
예를 들어, 표현식 (1+3)*2
은 다음과 같이 표현 될 수 있습니다.
*
/ \
+ 2
/ \
1 3
표현을 평가하기 위해 부모의 가치를 요구합니다. 이 노드는 자식, 플러스 연산자 및 단순히 '2'를 포함하는 노드에서 값을 가져옵니다. 더하기 연산자는 값이 '1'과 '3'인 하위 항목에서 값을 가져 와서 더한 다음 4를 곱셈 노드로 반환하여 8을 반환합니다.
이진 트리의 사용은 동작이 수행되는 순서가 동일하다는 의미에서 광택 표기법을 역전시키는 것과 유사하다. 또한 주목할 점은 반드시 이진 트리 일 필요는 없으며, 가장 일반적으로 사용되는 연산자는 이진일뿐입니다. 가장 기본적인 수준에서 이진 트리는 실제로 매우 단순한 순전히 기능적인 프로그래밍 언어입니다.
"순수한"이진 트리에 대한 사용이 없다고 생각합니다. (교육 목적 제외) Red-Black 트리 또는 AVL 트리 와 같은 균형 이진 트리는 O (로그) 작업을 보장하기 때문에 훨씬 유용합니다. 일반적인 이진 트리는 목록 (또는 거의 목록)이 될 수 있으며 많은 데이터를 사용하는 응용 프로그램에는 실제로 유용하지 않습니다.
균형 트리는 종종 맵이나 세트를 구현하는 데 사용됩니다. 또한 O (nlogn)로 정렬하는 데 사용할 수 있으며 더 나은 방법이 있습니다.
또한 검색 / 삽입 / 삭제를 위해 해시 테이블을 사용할 수 있으며, 일반적으로 이진 검색 트리 (균형 또는 불확실성)보다 성능이 좋습니다.
검색 / 삽입 / 삭제 및 정렬이 필요한 경우 (균형) 이진 검색 트리가 유용한 응용 프로그램입니다. 준비된 빌드 균형 트리가 주어지면 정렬이 제자리에있을 수 있습니다 (거의 재귀에 필요한 스택 공간을 무시 함). 그것은 여전히 O (nlogn) 일 것이지만 더 작은 상수 요소와 추가 공간이 필요하지 않습니다 (데이터를 배열에 넣어야한다고 가정하면 새 배열 제외). 반면 해시 테이블은 (적어도 직접적으로는) 정렬 할 수 없습니다.
어쩌면 그것들은 무언가를하는 정교한 알고리즘에도 유용하지만, tbh 아무것도 내 마음에 오지 않습니다. 더 많은 것을 찾으면 내 게시물을 편집 할 것입니다.
fe B + trees와 같은 다른 트리는 데이터베이스에서 널리 사용됩니다.
가장 일반적인 응용 프로그램 중 하나는 저장된 요소에 빠르게 액세스하고 검색하기 위해 정렬 된 형식으로 데이터를 효율적으로 저장하는 것입니다. 예를 들어 std::map
또는 std::set
C ++ 표준 라이브러리에 있습니다.
데이터 구조 인 이진 트리는 다양한 식 파서 및 식 솔버 구현에 유용합니다.
인덱싱과 같은 일부 데이터베이스 문제를 해결하는 데 사용될 수도 있습니다.
일반적으로 이진 트리는 특정 트리 기반 데이터 구조의 일반적인 개념이며 다양한 특정 유형의 이진 트리를 다른 속성으로 구성 할 수 있습니다.
현대 하드웨어에서 이진 트리는 캐시 및 공간 동작이 잘못되어 거의 항상 차선책입니다. 이것은 (반) 균형 변형에도 적용됩니다. 그것들을 찾으면 성능이 중요하지 않은 곳 (또는 비교 기능에 의해 지배되는 곳)이거나 역사적이거나 무지한 이유가 있습니다.
AST를 표현하기 위해 이진 트리를 사용하는 컴파일러는 postorder, inorder와 같이 트리를 구문 분석하기 위해 알려진 알고리즘을 사용할 수 있습니다. 프로그래머는 자체 알고리즘을 만들 필요가 없습니다. 소스 파일의 이진 트리가 n-ary 트리보다 높기 때문에 빌드하는 데 시간이 더 걸립니다. selstmnt : = "if" "("expr ")"stmnt "ELSE"stmnt 이진 트리에는 3 레벨의 노드가 있지만 n-ary 트리의 레벨은 1입니다 (chids).
그렇기 때문에 Unix 기반 OS가 느립니다.