diff --git "a/data-structure/\354\212\271\355\227\214/AdjacencyList.md" "b/data-structure/\354\212\271\355\227\214/AdjacencyList.md" new file mode 100644 index 0000000..959db70 --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/AdjacencyList.md" @@ -0,0 +1,158 @@ +## 인접리스트(adjacency list) + +그래프를 표현하는 주요 방법 중 하나 + +그래프에서 정점과 간선의 관계를 연결리스트로 나타냄 + +### 인접리스트란? + +각 정점에 연결된 정접들의 리스트를 저장하는 방식 + +```jsx +// 그래프: +// 0 - 1 - 2 +// | | +// 3 - 4 + +// 인접리스트 표현: +0: [1, 3] +1: [0, 2, 4] +2: [1] +3: [0, 4] +4: [1, 3] +``` + +### 인접리스트가 아닌 vector도 되는 이유? + +> 각 정점마다 연결된 정점들을 저장 + +위 개념에 따르면, 연결리스트던 동적배열(vector)을 사용하던 구조적으로 같은 역할을 함 + +아래와 같은 이유로 vector를 더 선호함 + +- 캐시 지역성이 좋음 +- 구현이 간단 +- 랜덤접근 가능 + +### 연결리스트의 시간복잡도 + +- n번째 인덱스에 삽입, 삭제 : O(1) +- 마지막 요소에 삽입, 삭제 : O(1) +- 특정요소 탐색 : O(n) +- n번째 요소 참조 : O(n) + +### vector의 시간복잡도 + +- n번째 인덱스에 삽입, 삭제 : O(n) +- 마지막 요소에 삽입, 삭제 : O(1) +- 특정요소 탐색 : O(n) +- n번째 요소 참조 : O(1) + +> 인접리스트에서 많이 사용되는 연산은 (1)마지막요소 삽입과 (2)탐색 연산이라 vector로 구현해도 됨 + +### 인접리스트의 탐색방법 + +```cpp +// DFS (깊이 우선 탐색) +void dfs(int node, vector adj[], bool visited[]) { + visited[node] = true; + for(int next : adj[node]) { + if(!visited[next]) { + dfs(next, adj, visited); + } + } +} + +// BFS (너비 우선 탐색) +void bfs(int start, vector adj[]) { + queue q; + bool visited[MAX] = {false}; + + q.push(start); + visited[start] = true; + + while(!q.empty()) { + int node = q.front(); + q.pop(); + + for(int next : adj[node]) { + if(!visited[next]) { + visited[next] = true; + q.push(next); + } + } + } +} +``` + +## 인접행렬 vs 인접리스트 + +### 공간복잡도 + +| | | | +| -------------- | ------ | --------------------- | +| **인접행렬** | O(V²) | V×V 크기의 2차원 배열 | +| **인접리스트** | O(V+E) | 정점 수 + 간선 수만큼 | + +### 시간복잡도 + +| | | | +| --------------------------------- | ----- | --------- | +| **두 정점의 연결 확인** | O(1) | O(degree) | +| **한 정점의 모든 인접 정점 찾기** | O(V) | O(degree) | +| **간선 추가** | O(1) | O(1) | +| **간선 삭제** | O(1) | O(degree) | +| **전체 그래프 순회** | O(V²) | O(V+E) | + +### 인접행렬을 사용하는 경우 + +- 정점 수가 적을 때 (V ≤ 1000 정도) ⇒ 그래프가 희소(sparse) 할 때 +- 간선 유무를 자주 확인해야 할 때 +- 밀집 그래프(dense graph)일 때 +- 플로이드-워셜 같은 알고리즘 + +### 인접리스트를 사용하는 경우(더 일반적으로 사용함) + +- 정점 수가 많을 때 ⇒ 그래프가 조밀(dense) 할 때 +- 희소 그래프일 때 +- DFS, BFS, 다익스트라 등 대부분의 그래프 알고리즘 +- **코딩 테스트에서 대부분의 경우** + +--- + +### 희소/밀집 그래프? + +**희소 그래프 (Sparse Graph)** + +- 정점(노드)의 수에 비해 간선이 상대적으로 적은 그래프 +- 간선 수가 최대 가능한 간선 수보다 훨씬 적은 경우 +- 수학적으로는 보통 E ≪ V² (E는 간선 수, V는 정점 수) +- 예: + - 정점이 1,000개인데 간선이 2,000개 정도인 경우 → 희소 그래프 +- 특징: + - 메모리를 절약하려면 인접 리스트가 유리함 + - 인접 행렬로 표현하면 대부분의 셀이 0(즉, 연결 안 된 상태)이라 공간 낭비가 큼 + - 예시: 소셜 네트워크에서 일반 유저 간 친구 관계 (모두가 서로 친구는 아님) + +**밀집 그래프 (Dense Graph)** + +- 정점의 수에 비해 간선이 많은 그래프 +- 간선 수가 거의 최대치에 가까운 경우 +- 보통 E ≈ V² 수준 +- 예: + - 정점이 100개인데 간선이 4,000개 이상이면 (100² = 10,000 중 절반 이상 연결) 밀집 그래프라고 봄 +- 특징: + - 인접 행렬이 탐색에 유리 (모든 노드 간 연결 여부를 빠르게 확인 가능) + - 인접 리스트는 저장공간이 커지고 탐색 시 모든 리스트를 순회해야 해서 비효율적 + - 예시: 완전 그래프(모든 정점이 서로 연결된 그래프) + +--- + +### 컬렉션? + +Array, Map, Set 같은 건 “자료구조를 기반으로 한 컬렉션 객체” + +| 구분 | 설명 | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **자료구조 (Data Structure)** | 데이터를 저장하고 관리하기 위한 **구조적 개념**
_e.g. 배열(Array), 연결 리스트(Linked List), 스택(Stack), 큐(Queue), 트리(Tree), 그래프(Graph) 등_ | +| **컬렉션 (Collection)** | 여러 데이터를 모아 관리하는 **구현체나 컨테이너** 개념. 주로 고급 언어나 런타임에서 제공하는 **구체적인 자료구조 구현체**를 말함.
_e.g. JavaScript의 `Array`, `Map`, `Set` 같은 것들_ | diff --git "a/data-structure/\354\212\271\355\227\214/AdjacencyMatrix.md" "b/data-structure/\354\212\271\355\227\214/AdjacencyMatrix.md" new file mode 100644 index 0000000..fb0f96a --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/AdjacencyMatrix.md" @@ -0,0 +1,64 @@ +## 인접행렬(adjacency matrix) + +그래프를 표현하는 대표적인 방법 중 하나. + +그래프의 연결 관계를 2차원 배열(행렬)로 나타냄. + +### 기본 개념 + +정점이 n개인 그래프가 있을 때, $n*n$의 행렬로 표현 + +행렬의 `(i, j)` 위치 값은 정점 `i`에서 정점 `j`로 가는 간선의 존재 여부나, 가중치를 나타냄 + +### 표현 방식 + +**무방향 그래프(Undirected Graph)** + +- 간선이 있으면 1, 없으면 0 +- 대칭 행렬 형태 (`matrix[i][j] = matrix[j][i]`) + +**방향 그래프(Directed Graph)** + +- i → j 간선이 있으면 `matrix[i][j] = 1` +- 비대칭 행렬일 수 있음 + +**가중치 그래프(Weighted Graph)** + +- 간선의 가중치 값을 저장 +- 간선이 없으면 0 또는 ∞(무한대) + +### 예시 + +정점 A, B, C, D가 있고 A-B, A-C, B-D 간선이 있다면: + +```tsx +// 표로 나타내면 + A B C D +A [ 0 1 1 0 ] +B [ 1 0 0 1 ] +C [ 1 0 0 0 ] +D [ 0 1 0 0 ] + +// 코드로 표현하면 +bool a[4][4] = { + {0, 1, 1, 0}, + {1, 0, 0, 1}, + {1, 0, 0, 0}, + {0, 1, 0, 0}, +}; +``` + +- `(1, 1)`, `(2, 2)`와 같은 자기 자신을 나타내는 위치(대각선 원소)는, +정점의 사이클이 있는 경우 1로 표기하고, 사이클이 없는 경우(트리) 0으로 표기함 + +### **장점** + +- 두 정점 간 연결 여부를 O(1)에 확인 가능 +- 구현이 간단하고 직관적 +- 간선 추가/삭제가 $O(1)$ + +### **단점** + +- 공간 복잡도가 $O(n²)$로 비효율적 +- 정점이 많고 간선이 적은 희소 그래프에서 메모리 낭비 +- 모든 간선을 확인하려면 $O(n²)$ 시간 필요 \ No newline at end of file diff --git "a/data-structure/\354\212\271\355\227\214/Graph.md" "b/data-structure/\354\212\271\355\227\214/Graph.md" new file mode 100644 index 0000000..f52f789 --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/Graph.md" @@ -0,0 +1,34 @@ +## 그래프이론의 기초 + +### 그래프의 종류 + +| | | +| --------------------------------- | ----------------------------------------- | +| **무방향 그래프** | 간선에 방향 없음. A—B는 B—A와 동일 | +| **방향 그래프** | 간선에 방향 존재. A→B와 B→A는 다름 | +| **가중치 그래프(Weighted Graph)** | 간선마다 비용(weight)이 존재 | +| **비가중치 그래프** | 모든 간선의 비용이 동일하거나 중요치 않음 | + +### 정점(Vertex) + +그래프의 구성 요소(노드) + +- 분할할 수 없는 객체 +- “점”으로 표현되는 위치, 사람, 물건 등 + +### 간선(Edge) + +- 정점 간의 연결 관계 +- 정점을 잇는 선, 관계, 경로 등 + +### indegree outdegree + +- 정점으로 나가는 간선을 outdegree +- 들어오는 간선을 indegree +- 간선의 개수에 따라 수치(차수)로 표현하기도 함 + +### 가중치(Weight) + +- 정점과 정점 사이에 드는 비용을 말함 +- 거리/비용/시간 등 “연결의 강도”를 나타냄 +- 최단 경로 알고리즘에서 핵심 개념 diff --git "a/data-structure/\354\212\271\355\227\214/HashTable.md" "b/data-structure/\354\212\271\355\227\214/HashTable.md" new file mode 100644 index 0000000..267ad84 --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/HashTable.md" @@ -0,0 +1,115 @@ +# 해시테이블 + +‘키(key)’와 ‘값(value)’의 쌍으로 데이터를 저장하는 자료구조 + +검색, 삽입, 삭제 같은 연산을 평균적으로 O(1)시간에 처리가 가능함 + +- 키(key): 데이터 식별을 위한 고유한 값 +- 값(value): 실제로 저장되는 데이터 + +*e.g.* + +- ***자바스크립트:** Map, Object* +- ***파이썬:** dict* +- ***자바:** HashMap, Hashtable* + +## 해시, 해싱, 해싱함수 + +### 해시 + +- 다양한 길이를 가진 데이터를 고정된 길이를 가진 데이터로 매핑한 값 + +### 해싱 + +- 임의의 데이터를 해시로 바꾸는 작업 + +### 해시함수 + +- 임의의 데이터를 입력으로 받아 **일정한 길이의 데이터(인덱스)**로 바꿔주는 함수 + +--- + +## 시간복잡도 + +해시테이블읜 최악과 평균의 차이가 커서, 이를 고려해 사용 여부를 결정하는게 좋음 + +### 평균시간복잡도 + +- 참조 : $O(1)$ +- 탐색 : $O(1)$ +- 삽입 / 삭제 : $O(1)$ + +### 최악의 시간복잡도 + +만약 해시테이블에서 충돌이 많이 일어난다면 해당 해시값이 같은 모든 요소들을 탐색해야 +하므로 $O(n)$의 시간복잡도가 소요됨 + +- 참조 : $O(n)$ +- 탐색 : $O(n)$ +- 삽입 / 삭제 : $O(n)$ + +--- + +## 충돌 문제 해결방법 + +해시 함수가 서로 다른 키를 같은 인덱스로 매핑할 수 있는데, 이걸 **충돌**이라고 함 + +### 1. 체이닝(Chaining) + +충돌시 연결리스트에 할당하고 충돌시 연결리스트를 탐색하는 기법 + +- 장점: 구현이 간단, 해시테이블에 많은 데이터를 넣을 수 있음 +- 단점: 연결리스트기반이라 캐시성능이 좋지 않음 체인이 길어지면 최악의 경우 +$O(n)$ + + ```jsx + { + key: "apple", + value: 100, + next: { key: "banana", value: 200, next: null } + } + // 이런식으로 저장함 + ``` + + +### 2. 개방주소법(Open Addressing) + +개방주소법(open addressing)은 충돌시 다른 버켓에 데이터를 삽입하는 기법 + +1. 선형 탐색(Linear Probing) + - 해시충돌 시 다음 버켓, 혹은 몇 개를 "선형적으로” 건너뛰어 데이터를 삽입 + ex) 1, 2, 3, 4 +2. 제곱 탐색(Quadratic Probing) + - 해시충돌 시 1부터 연속적인 수를 만들며 해당 수의 제곱만큼 건너뛴 버켓에 데이터를 + 삽입 + ex) 1,4,9,16... +3. 이중 해싱(Double Hashing) + - 해시충돌 시 다른 해시함수를 한 번 더 적용한 결과를 이용해서 데이터를 삽입 + +### 무한 순환이 발생하면? + +1. 테이블이 꽉찬경우 + 1. 에러 리턴 + 2. 테이블 크기 리사이징 +2. 해시함수가 하자라서 순환하는 경우 + 1. 테이블 크기는 소수로 잡기 + + ```jsx + // e.g. 제곱탐사 + index = (h(key) + i²) % tableSize + tableSize = 8 + h(key) = 0 + + >> 0, 1, 4, 1, 0, 1, 4, 1, ... 한무 반복 + ``` + + 2. 다른 탐사 방식으로 전환 + +## 장단점 + +| **구분** | **장점** | **단점** | +| --- | --- | --- | +| 해시테이블 | 평균적으로 빠른 검색/삽입/삭제 (O(1)) | 해시 충돌 시 성능 저하, 해시 함수 설계 중요 | +| 배열 대비 | 인덱스 대신 키로 접근 가능 | 순서가 유지되지 않음 | + +--- \ No newline at end of file diff --git "a/data-structure/\354\212\271\355\227\214/Heap.md" "b/data-structure/\354\212\271\355\227\214/Heap.md" new file mode 100644 index 0000000..daefd0c --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/Heap.md" @@ -0,0 +1,38 @@ +# 힙(Heap) + +완전이진트리(Complete binary tree) 형태를 가지며, + +각 노드가 특정 규칙에 따라 정렬된 자료구조 + +## 종류 + +| **종류** | **특징** | **루트 노드** | +| --- | --- | --- | +| **최대 힙 (Max Heap)** | 부모 ≥ 자식 | 최댓값 | +| **최소 힙 (Min Heap)** | 부모 ≤ 자식 | 최솟값 | + +## 힙의 데이터 삽입 + +1. 리프노드에 삽입할 노드를 삽입 +2. 해당노드와 부모노드를 서로 비교 +3. 정렬이 맞으면 그대로 두고, 그렇지 않으면 부모노드와 교환 +4. 정렬이 맞을 때까지 3번 과정을 반복 + +## 힙의 데이터 삭제 + +1. 루트 노드를 제거 +2. 루트 자리에 가장 마지막 노드를 삽입 +3. 올라간 노드와 그의 자식 노드과 비교 +4. 규칙을 만족하면 그대로 두고, 그렇지 않으면 자식노드와 교환 + +## 이진탐색트리와 차이점 + +이진탐색트리는 왼쪽이 더 작고 오른쪽이 더 큰 값으로 구성이 되지만 + +이와 유사한 최소힙의 경우 좌/우 구분 없음 + +## 시간복잡도 + +- 참조(최대 또는 최소값을 참조) : $O(1)$ +- 탐색 : $O(n)$ +- 삽입 / 삭제 : $O(logn)$ \ No newline at end of file diff --git "a/data-structure/\354\212\271\355\227\214/MapSet.md" "b/data-structure/\354\212\271\355\227\214/MapSet.md" new file mode 100644 index 0000000..81140d9 --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/MapSet.md" @@ -0,0 +1,76 @@ +## 맵(Map) + +a.k.a. 딕셔너리 + +`key-value` 쌍을 저장하는 자료구조 + +### 특징 + +- `Key`는 유일함(중복 불가) +- Key를 통해 Value에 빠르게 접근할 수 있음 +- Key기준 자동 정렬 가능, 삽입 시 마다 자동 정렬 +- 레드 - 블랙 트리로 구현 +- 대괄호 연산자로 Key로 직접 참조 가능 + +### 시간복잡도 + +- 참조 : $O(logN)$ +- 탐색 : $O(logN)$ +- 삽입 / 삭제 : $O(logN)$ + +### 활용 + +- 빈도 카운팅 + ```cpp + // 문자열에서 각 문자의 개수 세기 + string str = "hello world"; + map freq; + + for(char c : str) { + freq[c]++; + } + + // 결과 + // ' ': 1 + // 'd': 1 + // 'e': 1 + // 'h': 1 + // 'l': 3 + // 'o': 2 + // 'r': 1 + // 'w': 1 + ``` + +## 셋(Set) + +중복을 허용하지 않는 값들의 모음 + +### 특징 + +- 중복 값 자동 제거 +- 자동 정렬, 삽입순서 유지 가능 +- 값의 존재 여부를 빠르게 확인할 수 있음 + +### 시간복잡도 + +- 참조 : $O(logN)$ +- 탐색 : $O(logN)$ +- 삽입 / 삭제 : $O(logN)$ + +### 활용 + +```cpp +// O(N²) 방식 +vector v = {1, 2, 3, 4, 5}; +for(int i = 0; i < v.size(); i++) { + for(int j = 0; j < v.size(); j++) { + if(v[i] == target) { /* ... */ } + } +} + +// O(N log N) 방식 - set 사용 +set s(v.begin(), v.end()); +if(s.find(target) != s.end()) { + // 존재함 +} +``` diff --git "a/data-structure/\354\212\271\355\227\214/StackQueue.md" "b/data-structure/\354\212\271\355\227\214/StackQueue.md" new file mode 100644 index 0000000..d64c0ae --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/StackQueue.md" @@ -0,0 +1,41 @@ +## 스택 +- LIFO(Last In, First Out) 구조 + → 나중에 들어온 데이터가 먼저 나감 +- _e.g._ 브라우저 뒤로가기, 함수 호출 스택 … + +### 메서드와 시간복잡도 +- 탐색(search): $O(n)$ + ⇒ 특정 요소 찾기 + +**메서드 >>** +- `push(value)` : $O(1)$ + ⇒ 맨 위(top)에 요소 추가 +- `pop()`: $O(1)$ + ⇒ 맨 위 요소 제거 +- `peek()`, `top()`: $O(1)$ + ⇒ 맨 위 요소 조회 +- `isEmpty()`: $O(1)$ + ⇒ 비었는지 확인 +- `size()`: $O(1)$ + ⇒ 요소 개수 반환 + +## 큐 +- **FIFO**(First In, First Out) 구조 + → 먼저 들어온 데이터가 먼저 나감 +- _e.g._ 프린터 출력 대기열, 프로세스 스케줄, 네트워크 요청 큐 … + +### 메서드와 시간복잡도 +- 탐색(search): $O(n)$ + ⇒ 특정 요소 찾기 + +**메서드 >>** +- `enqueue()`: $O(1)$ + ⇒ 큐의 뒤(rear)에 요소 추가 +- `dequeue()`: $O(1)$ + ⇒ 큐의 앞 요소 제거 및 반환 +- `peek()`, `front()`: $O(1)$ + ⇒ 첫 요소 조회 +- `isEmpty()`: $O(1)$ + ⇒ 비었는지 확인 +- `size()`: $O(1)$ + ⇒ 요소 개수 반환 \ No newline at end of file diff --git "a/data-structure/\354\212\271\355\227\214/Tree.md" "b/data-structure/\354\212\271\355\227\214/Tree.md" new file mode 100644 index 0000000..5c0d393 --- /dev/null +++ "b/data-structure/\354\212\271\355\227\214/Tree.md" @@ -0,0 +1,219 @@ +## 트리(Tree) + +- 자식 - 부모 노드로 이루어진 계층 구조 +- 사이클 없는 **연결된** 무방향 그래프 +- 트리는 반드시 **연결되어 있어야** 하며, 연결되지 않으면 **숲(forest)**가 됨 + +### 특정 + +1. 부모, 자식 계층 구조를 가짐 +2. 간선 수 = `노드 수 - 1 ($V - 1 = E$ )` +3. 임의의 두 노드 사이에 경로가 **정확히 하나** 존재함 + +### 루트노드, 내부노드, 리프노드 + +- **루트 노드**: 가장 위에 있는 노드, 탐색의 기준이 되는 노드 +- **내부 노드**: 루트와 리프 사이에 있는 노드 +- **리프 노드**: 자식 노드가 없는 노드 + +### 트리의 높이와 레벨, 깊이 + +- **깊이** + - 트리에서의 깊이는 각 노드마다 다름 + - 루트에서 특정 노드까지 최단거리로 갔을 때의 거리를 말함 +- 높이 + - 루트부터 리프까지 거리 중 가장 긴 거리 +- 레벨 + - 보통 깊이와 같은 의미를 가짐 +- 서브트리 + - 트리 내의 하위 집합을 말함 + - 트리 내의 부분집합으로 표현할 수 있음 + +### 차수(Degree) + +- 노드의 차수: 해당 노드에 연결된 간선의 수 +- 트리의 차수: 트리 내 노드들의 차수 중 최댓값 + +### 숲 + +트리로 이루어진 집합을 숲(forest)라고 함 + +--- + +## 이진트리와 이진탐색트리 + +각 노드의 자식노드가 2개 이하로 구성되어있는 트리 + +### 종류 + +- 정 이진트리(full binary tree) + - 자식 노드가 0개 또는 2개인 이진 트리 +- 완전 이진트리(complete binary tree) + - 왼쪽에서부터 채워져 있는 이진 트리 + - 마지막 레벨을 제외, 왼쪽부터 채워져 있는 트리 +- 편향 이진트리(degenerate binary tree) + - 자식 노드가 모두 하나만 존재하는 트리 +- 포화 이진트리(perfect binary tree) + - 모든 자식 노드가 2개로 가득 차있는 트리 +- 균형 이진트리(balanced binary tree) + - 모든 노드의 왼쪽 하위 트리와 오른쪽 하위 트리의 **높이 차이**가 1이하인 트리 + - _e.g._ 레드블랙트리 + +### 이진트리 순회(Traversal) 방법 + +- 전위 순회(Preorder): 루트 → 왼쪽 → 오른쪽 +- 중위 순회(Inorder): 왼쪽 → 루트 → 오른쪽 +- 후위 순회(Postorder): 왼쪽 → 오른쪽 → 루트 +- 레벨 순회(Level-order): BFS 방식으로 레벨별 순회 + +### 이진탐색트리(BST, Binary Search Tree) + +이진 트리의 일종으로, + +- 노드의 “오른쪽 하위 트리에는 노드보다 큰 값”이 +- “왼쪽 하위 트리에는 노드보다 작은 값”이 들어있는 트리 + +⇒ 특정 값을 검색할 때 대소비교로 쉽게 찾을 수 있음 + +### 이진탐색트리의 시간복잡도 + +- 균형잡힌 분포를 가지는 경우 탐색/삽입/삭제/수정 모두 $O(logN)$ +- 다만 삽입 순서에 따라서 선형적인 구조를 가질 수 있음 +- 이를 개선하기 위해 AVL트리, 레드블랙트리 등의 구조로 균형잡힌 트리를 만들 수 있음 +- 최악의 경우: 편향 트리(skewed tree)가 될 때 + +| 연산 | 평균 | 최악 | +| ---- | -------- | ---- | +| 탐색 | O(log n) | O(n) | +| 삽입 | O(log n) | O(n) | +| 삭제 | O(log n) | O(n) | + +### AVL트리 + +- 균형 이진 탐색 트리의 일종 +- 편향 이진 트리가 되어 높이가 높아지는것을 방지하기 위해 사용 +- 모든 노드의 왼쪽과 오른쪽 서브트리의 높이 차이가 1 이하로 유지 +- 높이 차이가 1보다 커지면 회전을 통해 높이차이를 줄임 + +### 레드블랙트리 + +- 2-3-4 트리를 이진 트리로 표현한 것 +- 노드의 상태나 속성을 색상(빨강, 검정)으로 표현함 +- 루트/리프노드는 항상 검은색, 빨간 노드의 자식은 항상 검은색(No Double Red) +- 모든 리프노드에서 루트까지 경로의 검은색 노드의 개수가 같음 + → 2-3-4트리의 리프노드가 모두 같은 깊이에 있음(균형트리임)을 나타냄 + +--- + +## **DFS (Depth-First Search, 깊이 우선 탐색)** + +한 경로를 끝까지 파고들다가 더 이상 진행할 수 없을 때 + +다시 돌아(backtrack)와서 다른 경로를 탐색하는 방식. + +즉, **“깊이”를 우선으로 탐색** + +### **탐색 과정 예시 (스택 또는 재귀 이용):** + +1. 시작 노드 방문 +2. 인접 노드 중 방문하지 않은 노드로 이동 +3. 더 이상 갈 곳이 없으면 이전 노드로 되돌아감 (백트래킹) +4. 모든 노드를 방문할 때까지 반복 + +**예시 (A → B → D → E → C 순서로 탐색):** + +``` + A + / \ + B C + | + D + | + E +``` + +→ A에서 시작 → B로 내려감 → D → E → (더 이상 없음) → 뒤로 돌아가 C 탐색 + +### **구현 방식** + +- **재귀**로 구현하기 쉬움 +- **스택(Stack)** 을 이용한 반복문으로도 가능 + +```jsx +function dfs(graph, start, visited = new Set()) { + visited.add(start); + console.log(start); + + for (const neighbor of graph[start]) { + if (!visited.has(neighbor)) dfs(graph, neighbor, visited); + } +} +``` + +**특징 요약:** + +- **스택 구조 (혹은 재귀 호출 스택)** 사용 +- **모든 경로를 깊이 탐색**하기에 경로 찾기나 순열 탐색 등에 유용 +- **공간 효율성**은 BFS보다 좋을 때도 있음 (그래프 밀도에 따라 다름) + +--- + +## **BFS (Breadth-First Search, 너비 우선 탐색)** + +현재 노드의 **모든 인접 노드를 먼저 방문한 후**, + +그 다음 단계의 노드들을 탐색하는 방식. + +즉, **“너비”를 우선으로 탐색**해. + +### **탐색 과정 예시 (큐 이용):** + +1. 시작 노드를 큐에 넣고 방문 +2. 큐에서 하나 꺼내 인접 노드를 모두 큐에 추가 +3. 큐가 빌 때까지 반복 + +**예시 (A → B → C → D → E 순서로 탐색):** + +``` + A + / \ + B C + | + D + | + E +``` + +→ A에서 시작 → 같은 레벨(B, C) 먼저 → 그다음 D → 마지막 E + +### **구현 방식:** + +- **큐(Queue)** 를 사용 + +```jsx +function bfs(graph, start) { + const visited = new Set(); + const queue = [start]; + visited.add(start); + + while (queue.length > 0) { + const node = queue.shift(); + console.log(node); + + for (const neighbor of graph[node]) { + if (!visited.has(neighbor)) { + visited.add(neighbor); + queue.push(neighbor); + } + } + } +} +``` + +**특징 요약:** + +- **큐 구조** 사용 +- 최단 경로(가중치 없는 그래프에서)를 찾는 데 유리 +- 계층 구조 탐색, 예: 소셜 네트워크 친구 탐색, 최단 거리 계산 등에 자주 사용 + +---