용어정리
- 서브 트리 : 하나의 노드와 그 노드들의 자손들로 이루어진 트리
- 단말 노드 : 자식이 없는 노드( leaf node)라고도 불림
- 비단말 노드 : 적어도 하나의 자식을 갖는 노드
- 레벨 : 트리의 각층의 번호
- 높이 : 트리의 최대 레벨 h
- 차수 : 노드가 가지고 있는 자식 노드의 개수
이진 트리
: 모든 노드의 차수가 2 이하가 되어 구현하기에 편리하다는 장점
특징
1. 노드의 개수가 n개이면 간선의 개수는 n-1개
2. 높이가 h인 이진트리의 경우, 최소 h개의 노드, 최대 2h-1개의 노드를 갖는다.
3. n개의 노드를 가지는 이진트리의 높이는 최대 n이거나 최소 ┌log₂(n+1)┐
-> 높이가 h일 때(노드의 개수 h) 최대 2^h-1개의 노드
따라서 2^h = logn(n+1)
만약 h가 7일때 최대 높이는 노드를 한쪽 서브트리로 하나씩 계속 나열하는 경우이다. 이 경우일 때 레벨이 많아지므로 데이터가 매우 많아질 경우가 생김. (2^h = n+1)
이때 로그를 취하면 데이터를 효과적으로 줄일 수 있다(이진트리, logn(n+1))
이진트리의 표현
- 배열 표현법 : 모든 이진트리를 포화이진트리라고 가정했을 때, 각 노드에 번호를 붙여서 그 번호를 배열의 인덱스로 삼아 노드의 데이터를 배열에 저장하는 방법
- 링크 표현법 : 포인터를 이용하여 자식노드<-부모노드
이진트리의 순회
순회 : 트리의 노드들을 체계적으로 방문
3가지(V : 루트, L : 왼쪽 서브트리, R : 오른쪽 서브트리)
1. 전위순회(VLR) : 자손노드보다 루트노드를 먼저 방문한다
2. 중위순회(LVR) : 왼쪽 자손, 루트, 오른쪽 자손 순으로 방문한다
3. 후위순회(LRV) : 루트노드보다 자손을 먼저 방문한다.
4. 층별순회 : 위쪽 노드들 부터 순서대로 아래로 방문한다.
수식트리계산
- 후위순회를 사용한다
ex) (3*2) + (5X6)
후위순회를 사용하면 >> 3 2 * 5 6 X +
=> evaluate(exp)
if exp is 리프노드
then return exp->data;
else x<-evaluate(exp->left); //x는 왼쪽 자식 계산 후 저장(숫자)
y<-evaluate(exp->right); //y는 오른쪽 자식 계산 후 저장(숫자)
op<-exp->data; //본인(연산자)
return(x op y); //리턴 값 (숫자)
이진트리연산: 노드 개수
: 각각의 서브트리에 대하여 순환 호출한 다음, 반환되는 값에 1(본인)을 더하여 반환
=> int get_node_count(TreeNode *node)
{
int count = 0;
if(node != NULL)
count = 1 + get_node_count(node->left) + get_node_count(node->right); ...1은 본인을 나타냄(1 + L + R)
return count;
}
이진트리연산: 높이
: 서브트리에 대해 순환호출을 하고 서브트리들의 반환값 중 에서 최대값에 1을 더해서 반환.
=> int get_height(TreeNode *node)
{
int height = 0;
if(node != NULL) {
height = 1 + max(get_height(node->left), get_height(node->right));
return height;
}
이진탐색트리(자료구조)
- key(왼쪽서브트리) <= key(루트노드) <= key(오른쪽서브트리)
- 중위순회로 이진탐색 >> 오름차순으로 정렬
탐색연산설명
- 종료조건 : 비교한 결과가 같으면 끝남
- 주어진 키 값 < 루트 노드의 키값 : 이 루트 노드의 왼쪽 자식을 기준으로 다시 탐색
- 주어진 키 값 > 루트 노드의 키값 : 이 루트 노드의 오른쪽 자식을 기준으로 다시 탐색
이진트리에서 노드 삽입/삭제
1. 삽입
- 탐색 후 탐색이 실패한 위치에 노드 삽입
2. 삭제 ***
3가지 경우
(1) 삭제하려는 노드가 단말 노드일 경우
: 단말 노드의 부모 노드를 찾아 연결을 끊는다
(2) 삭제하려는 노드가 하나의 서브트리만 가지는 경우
: 삭제되는 노드가 왼쪽이나 오른쪽 서브 트리중 하나만 가지고 있는 경우에는 노드는 삭제하고 서브 트리는 부모 노드에 붙여준다.
(3) 삭제하려는 노드가 두개의 서브 트리 모두 가지고 있는 경우
: 삭제노드와 가장 비슷한 값을 가진 노드를 삭제노드 위치로 가져온다. -> 왼쪽 서브트리에서 제일 큰 값( 삽입 시에 작은 노드가 왼쪽으로 옮겨가는 서브트리의 성질)과 오른쪽 서브트리에서 제일 작은 값( 삽입 시에 큰 노드가 오른쪽으로 옮겨가는 서브트리의 성질)을 비교하여 두 노드 중 큰 노드를 삭제노드 위치로 가져오는 방법을 채택.
>> 틀린방법 ㅡㅡ;;
BST 삭제 과정에서 **"왼쪽 서브트리에서 가장 큰 값"**(Inorder Predecessor)과 **"오른쪽 서브트리에서 가장 작은 값"**(Inorder Successor) 중 하나를 선택하여 삭제된 노드를 대체할 수 있습니다. 하지만 둘 중 하나를 선택해야 하며, **두 값을 비교해서 더 큰 노드를 대체 노드로 선택하는 것은 BST 규칙에 어긋납니다.**
### 이유:
BST의 속성은 왼쪽 서브트리는 루트보다 작고, 오른쪽 서브트리는 루트보다 커야 합니다.
따라서 대체 노드는 다음 중 하나여야만 합니다:
1. **왼쪽 서브트리에서 가장 큰 값** (루트보다 작음).
2. **오른쪽 서브트리에서 가장 작은 값** (루트보다 큼).
만약 "더 큰 값"을 선택한다면, 트리의 순서 속성이 깨질 위험이 있습니다.
### 예시:
#### 초기 트리:
```
12
/ \
5 15
/ \
3 7
```
#### 잘못된 대체 방식:
- **5를 삭제**하고 "7과 3 중 더 큰 값(=7)"을 선택해 대체:
```
12
/ \
7 15
/
3
```
위 트리는 `3 < 7 < 12`라는 BST 속성을 유지하므로 운 좋게 맞아떨어졌지만, 이 방법은 일반적으로 BST를 보장하지 않습니다. 대체로 **왼쪽 서브트리 또는 오른쪽 서브트리 중 하나를 일관되게 선택하는 것이 중요**합니다.
#### 올바른 방법:
1. **왼쪽 서브트리에서 가장 큰 값(=3)**으로 대체하거나,
2. **오른쪽 서브트리에서 가장 작은 값(=7)**으로 대체.
위 두 방법 중 하나만 선택해야 합니다.
이진탐색트리의 성능
- 이진탐색트리에서의 탐색, 삽입, 삭제 연산의 시간 복잡도는 트리의 높이를 h라고 했을 때 h에 비례한다 (탐색 시간 log2n ∝ h, 밑이2이고 진수가 n인 로그)
- 경우
- 최선의 경우 : 이진트리가 균형적으로 생성되어 있는 경우 h = log2n
- 최악의 경우 : 이진트리가 한쪽으로 치우친 경사이진트리의 경우 h = n (순차탐색과 시간복잡도가 같다)
'자료구조' 카테고리의 다른 글
[자료구조] 그래프 (1) | 2024.12.07 |
---|---|
[자료구조] 우선순위큐 & 힙(heap) (0) | 2024.12.02 |
덱 (deque) (0) | 2024.11.27 |
덱(deque) (0) | 2024.11.26 |
[자료구조] 큐 (0) | 2024.11.18 |