Skip to content

Commit b8d4bbb

Browse files
authored
Update 이진탐색트리(BST).md
1 parent d46d8ff commit b8d4bbb

File tree

1 file changed

+160
-47
lines changed

1 file changed

+160
-47
lines changed
Lines changed: 160 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,193 @@
1-
## 이진 탐색 트리 BST(Binary Search Tree)
1+
# 이진 탐색 트리 (Binary Search Tree, BST)
22

3-
**이진 탐색 트리의 목적은?**
3+
이진 탐색(binary search) ✚ 연결 리스트(linked list)를 결합한 이진트리
44

5-
-> 이진 탐색 + 연결 리스트
5+
이진 탐색 트리는 각 노드가 최대 두 개의 자식 노드를 가지는 트리 자료 구조
66

7-
- 이진 탐색
8-
- **탐색에 소요되는 시간 복잡도는 O(logN)**
9-
- 하지만 삽입, 삭제가 불가능.
10-
- 연결 리스트
11-
- **삽입, 삭제의 시간 복잡도는 O(1)**
12-
- 하지만 탐색하는 시간 복잡도는 O(N)
13-
- 이 두 가지를 합하여 장점을 모두 얻기 위해 고안된 것이 `이진 탐색 트리`
14-
- 즉, 효율적인 탐색 능력을 가지고 자료의 삽입, 삭제도 가능하게 만드는 것이다.
7+
![image](https://github.com/yeoseojeong/cpp-study/assets/121150215/af888cf3-0fb4-4a92-85d7-1b869074a323)
158

16-
![img](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2Fk074C%2FbtqwZZvI1D3%2FeVUanrpKdIRKnZpkKiQMe0%2Fimg.png)
179

18-
### [특징]
10+
## 특징
1911

20-
- 이진 트리의 일종으로 이진 탐색 트리에는 데이터를 저장하는 규칙이 있다.
2112

22-
- 이진 탐색 트리의 노드에 저장된 키는 유일하다.
23-
1. 루트 노드의 키가 왼쪽 서브 트리를 구성하는 어떠한 노드의 키보다 크다.
24-
2. 루트 노드의 키가 오른쪽 서브 트리를 구성하는 어떠한 노드의 키보다 작다.
25-
3. 왼쪽과 오른쪽 서브 트리도 이진 탐색 트리이다.
26-
- 탐색의 시간 복잡도는 O(logN)이다. 최악의 경우, 편향 트리가 되어 O(N)이 될 수도 있다.
27-
- 이진 탐색 트리의 순회는 **중위 순회**(in order) 방식이다.(왼쪽 - 루트 - 오른쪽)
28-
- 중위 순회로 **정렬된 순서**를 읽을 수 있다.
29-
- 중복된 노드가 없어야 한다.
13+
노드의 왼쪽 하위 트리에는 노드의 키보다 작은 키가있는 노드만 포함
3014

31-
**중복이 없어야 하는 이유는??**
15+
노드의 오른쪽 하위 트리에는 노드의 키보다 큰 키가있는 노드 만 포함
3216

33-
검색을 목적으로 하는 자료구조인데, 굳이 중복이 많은 경우에 이 트리를 사용해 검색 속도를 느리게 할 필요가 없다. 트리에 삽입하는 것보다 노드에 count를 가지게 하여 처리하는 것이 훨씬 효율적이다.
17+
왼쪽 및 오른쪽 하위 트리도 각각 이진 검색 트리여야함
3418

19+
중복된 키를 허용하지 않음
3520

21+
### 기본 연산
3622

37-
**[BST 핵심 연산]**
23+
#### 검색 (Search)
24+
이진 탐색 트리에서 특정 요소의 위치를 찾음
3825

39-
- 검색
40-
- 삽입
41-
- 삭제
42-
- 트리 생성
43-
- 트리 삭제
4426

27+
1. 루트에서 시작
28+
2. 검색 값을 루트와 비교하여 루트보다 작으면 왼쪽에 대해 재귀하고 크다면 오른쪽으로 재귀
29+
3. 일치하는 값을 찾을 때까지 절차를 반복
30+
4. 검색 값이 없으면 null을 반환
4531

4632

47-
**[시간 복잡도]**
33+
![image](https://github.com/yeoseojeong/cpp-study/assets/121150215/1df8dcbd-4824-401b-af40-8c80f1b7604e)
4834

49-
- 균등 트리 : 노드 개수가 N개일 때, O(logN)
50-
- 편향 트리 : 노드 개수가 N개일 때, O(N)
35+
```C
5136

52-
> 삽입, 검색, 삭제 시간 복잡도는 트리의 Depth에 비례.
37+
struct node* search (struct node* root, int key)
38+
{
39+
// root값이 null이거나 key값과 같다면 종료한다.
40+
if (root == NULL || root->data == key)
41+
return root;
5342

43+
// key가 root->data 보다 작으면 왼쪽 서브트리로 재귀한다.
44+
if (root->data > key)
45+
return search(root->left, key)
5446

47+
// key가 root->data 보다 크면 오른쪽 서브트리로 재귀한다.
48+
return search(root->left, key)
49+
}
5550

56-
삭제의 3가지 case
51+
```
5752
58-
1. 자식이 없는 단말 노드일 때 -> 그냥 삭제
59-
2. 자식이 1개인 노드일 때 -> 지워진 노드에 자식을 올리기
60-
3. 자식이 2개인 노드일 때 -> 오른쪽 자식 노드에서 가장 작은 값 or 왼쪽 자식 노드에서 가장 큰 값 올리기
6153
54+
#### 삽입
6255
56+
이진 검색트리에 데이터를 삽입하는 작업 (중복은 형용하지않음)
6357
64-
편향된 트리(정렬된 상태 값을 트리로 만들면 한쪽으로 뻗음)는 시간 복잡도가 O(N) 이므로 트리를 사용할 이유가 사라진다. 이를 바로 잡도록 도와주고 개선된 트리는 AVL Tree, RedBlack Tree가 있다.
58+
새 키는 항상 리프 노드에 삽입
6559
60+
1.Root에서 시작
61+
2.삽입 값을 루트와 비교합니다. 루트보다 작으면 왼쪽으로 재귀하고 크다면 오른쪽으로 재귀
62+
3.리프 노드에 도달한 후 노드보다 크다면 오른쪽에 작다면 왼쪽에 삽입
6663
64+
![image](https://github.com/yeoseojeong/cpp-study/assets/121150215/9153281f-6d43-4da3-9668-0062b93e1c0a)
6765
68-
**[이진 트리의 순회 방법]**
66+
```C
6967
70-
1. 전위 순회(Pre Order) : 루트 -> 왼쪽 -> 오른쪽
71-
2. 중위 순회(In Order) : 왼쪽 -> 루트 -> 오른쪽
72-
3. 후위 순회(Post Order) : 왼쪽 -> 오른쪽 -> 루트
68+
struct node {
69+
int data;
70+
struct node *left, *right;
71+
};
7372
74-
---
73+
// 새로운 BST node 생성
74+
struct node* newNode (int key) {
75+
struct node* temp = (struct *node)malloc(sizeof(struct node));
76+
temp->data = key;
77+
temp->left = NULL;
78+
temp->right = NULL;
79+
return temp;
80+
}
7581
76-
### 참조
82+
struct node* insert(struct node *root, int key) {
83+
// 트리가 비어있다면 새로운 노드를 만든다.
84+
if (root == NULL)
85+
return newNode(key);
7786
78-
정리가 너무 잘되어있어 이 부분은 참조하였습니다.
87+
// 루트값보다 크면 오른쪽으로 재귀하고, 작다면 왼쪽으로 재귀한다.
88+
if (key > root->data)
89+
root->right = insert(root->right, key);
90+
else if (key < root->data)
91+
root->left = insert(root->left, key);
92+
// 같은 값을 가지고 있는 경우 삽입을 하지 않는다.(중복 불가)
93+
return root;
94+
}
7995
80-
https://github.com/WooVictory/Ready-For-Tech-Interview/blob/master/Data%20Structure/%5BData%20Structure%5D%20%EC%9D%B4%EC%A7%84%20%ED%83%90%EC%83%89%20%ED%8A%B8%EB%A6%AC.md
96+
```
97+
98+
#### 삭제
99+
100+
101+
이진 검색 트리에서 특정 노드를 삭제
102+
103+
이진 검색 트리에서 노드를 삭제하는 세 가지 상황이 있음
104+
105+
**1. 삭제할 노드가 리프노드인 경우**
106+
107+
이 경우 노드를 삭제하기만 하면 됨
108+
109+
![image](https://github.com/yeoseojeong/cpp-study/assets/121150215/94809047-40f1-41c7-adc4-1a2722073ef3)
110+
111+
112+
**2.삭제할 노드에 자식이 하나만 있는 경우**
113+
114+
노드를 삭제하고 자식 노드를 삭제된 노드의 부모에 직접 연결
115+
116+
![image](https://github.com/yeoseojeong/cpp-study/assets/121150215/6527c7f0-517d-4c69-8bff-6fc4a021d337)
117+
118+
**삭제할 노드에 자식이 둘 있는 경우**
119+
120+
자식이 둘 있는 경우 successor 노드를 찾는 과정이 추가
121+
122+
123+
124+
#### surrcessor 노드란?
125+
126+
right subtree에 최소값
127+
128+
즉, inorder 순회에서 다음 노드를 말함
129+
130+
1. 삭제할 노드를 찾음
131+
2. 삭제할 노드의 successor 노드를 찾음
132+
3. 삭제할 노드와 successor 노드의 값을 바꿈
133+
4. successor 노드를 삭제
134+
135+
136+
![image](https://github.com/yeoseojeong/cpp-study/assets/121150215/7271d629-0bc5-4df0-8196-4da8f1d02ef1)
137+
138+
139+
```C
140+
141+
struct node {
142+
int data;
143+
struct node *left, *right;
144+
};
145+
146+
// 노드의 최소값을 가져오는 함수
147+
struct node* minValueNode (struct node* node){
148+
struct node* current = node;
149+
150+
while(current && current->left != NULL)
151+
current = current->left;
152+
153+
return current;
154+
}
155+
156+
struct node* deleteNode (struct node* root, int key) {
157+
// base case
158+
if(root == NULL)
159+
return root;
160+
// 삭제할 노드를 찾는다.
161+
if (key < root->data)
162+
root->left = deleteNode(root->left,key);
163+
164+
else if (key > root->data)
165+
root->right = deleteNode(root->right, key);
166+
167+
// 삭제할 노드를 찾은 경우
168+
else {
169+
struct node* temp;
170+
// 노드에 자식이 하나 이거나 없는 경우
171+
if (root->left == NULL) {
172+
temp = root->right;
173+
free(root);
174+
return temp;
175+
}
176+
else if (root->right == NULL) {
177+
temp = root->left;
178+
free(root);
179+
return temp;
180+
}
181+
182+
// 노드에 자식이 둘 있는 경우
183+
// successor 노드를 찾는다.
184+
temp = minValueNode(root->right);
185+
// successor 노드 키와 삭제할 노드 키를 바꾼다.
186+
root->key = temp->key;
187+
// 노드를 삭제한다.
188+
root->right = deleteNode(root->right, temp->key);
189+
}
190+
return root;
191+
}
192+
193+
```

0 commit comments

Comments
 (0)