fun dfs(graph: Array<List<Int>>, v: Int, visited: BooleanArray) {
visited[v] = true
println(v) // 처리 로직
for (neighbor in graph[v]) {
if (!visited[neighbor]) {
dfs(graph, neighbor, visited)
}
}
}
fun main() {
val graph = arrayOf(
listOf(), // 0번 노드 (사용 안 함)
listOf(2, 3, 4), // 1번 노드와 연결된 노드
listOf(1, 5),
listOf(1),
listOf(1),
listOf(2)
)
val visited = BooleanArray(graph.size)
dfs(graph, 1, visited)
}
✅ 언제 재귀 DFS를 선택할까? : 빠르게 구현할 수 있고, 문제 크기가 작을 때 유리
| 상황 | 이유 |
|---|---|
| 문제 조건이 작거나 노드 수가 적음 (예: ≤ 10⁴) | 재귀 호출이 간결하고 빠르게 작성 가능 |
| 백트래킹 문제 | 자연스럽게 "되돌아가는 구조"가 재귀에 잘 맞음 |
| 순열, 조합, 부분집합 | 함수 인자만으로 상태 관리가 쉬움 |
| 트리 탐색 (후위/전위 순회 등) | 트리는 깊이가 제한적이므로 재귀로 처리하기 좋음 |
import java.util.Stack
fun dfsStack(graph: Array<List<Int>>, start: Int) {
val visited = BooleanArray(graph.size)
val stack = Stack<Int>()
stack.push(start)
while (stack.isNotEmpty()) {
val v = stack.pop()
if (!visited[v]) {
visited[v] = true
println(v) // 처리 로직
// 인접 노드를 역순으로 넣어야 오름차순으로 방문됨
for (neighbor in graph[v].reversed()) {
if (!visited[neighbor]) {
stack.push(neighbor)
}
}
}
}
}
fun main() {
val graph = arrayOf(
listOf(),
listOf(2, 3, 4),
listOf(1, 5),
listOf(1),
listOf(1),
listOf(2)
)
dfsStack(graph, 1)
}
✅ 언제 스택 DFS를 선택할까? : 성능 안정성이 필요하거나, 방문 순서 세밀 제어가 필요할 때
| 상황 | 이유 |
|---|---|
| 깊은 그래프 (노드 수가 많거나, 깊이 10⁵ 이상 가능성) | 코틀린/JVM은 기본 재귀 깊이 제한 있음 (StackOverflow 방지) |
| 방문 순서를 정밀하게 제어해야 함 | 스택에 넣는 순서 조작 가능 (reversed() 등 활용) |
| 재귀가 금지된 환경 (예: 일부 코딩 테스트 플랫폼) | 명시적 스택으로 안전하게 처리 가능 |
| 디버깅이 복잡한 문제 | 상태를 명시적으로 관리하므로 디버깅이 용이함 |
📌 개념 요약
import java.util.LinkedList
import java.util.Queue
fun bfs(graph: Array<List<Int>>, start: Int) {
val visited = BooleanArray(graph.size)
val queue: Queue<Int> = LinkedList()
queue.add(start)
visited[start] = true
while (queue.isNotEmpty()) {
val v = queue.poll()
println(v) // 처리 로직
for (neighbor in graph[v]) {
if (!visited[neighbor]) {
queue.add(neighbor)
visited[neighbor] = true
}
}
}
}
fun main() {
val graph = arrayOf(
listOf(),
listOf(2, 3, 4),
listOf(1, 5),
listOf(1),
listOf(1),
listOf(2)
)
bfs(graph, 1)
}
| 항목 | DFS (재귀) | BFS (큐) |
|---|---|---|
| 구조 | 재귀 함수 호출 | Queue 사용 (LinkedList) |
| 사용처 | 백트래킹, 순열, 미로 등 | 최단 거리, 레벨 순회 등 |
| 방문 체크 | visited[v] = true 위치 중요 | 동일 |
📌 개념 요약
fun computePrefixSum(arr: IntArray): IntArray {
val prefixSum = IntArray(arr.size + 1) // 0번 인덱스는 0으로 비움
for (i in arr.indices) {
prefixSum[i + 1] = prefixSum[i] + arr[i]
}
return prefixSum
}
fun main() {
val arr = intArrayOf(3, 2, 4, 5, 1)
val prefixSum = computePrefixSum(arr)
// 예: 1번~3번 인덱스 합 (2 + 4 + 5 = 11)
val sum = prefixSum[4] - prefixSum[1]
println("부분합: $sum") // 11
}
🔷 개념
🔷 예제: 리스트 [1,2,3]에서 2개 숫자를 뽑는 모든 순서 출력
fun backtrack(arr: List<Int>, used: BooleanArray, path: List<Int>, m: Int) {
if (path.size == m) {
println(path)
return
}
for (i in arr.indices) {
if (!used[i]) {
used[i] = true
backtrack(arr, used, path + arr[i], m)
used[i] = false // 선택 취소 (되돌리기)
}
}
}
fun main() {
val arr = listOf(1, 2, 3)
val used = BooleanArray(arr.size)
backtrack(arr, used, emptyList(), 2)
}
출력
[1, 2]
[1, 3]
[2, 1]
[2, 3]
[3, 1]
[3, 2]
🔷 개념
🔷 예제: 피보나치 수열 (F(n) = F(n-1) + F(n-2))
val memo = IntArray(100) { -1 }
fun fib(n: Int): Int {
if (n <= 1) return n
if (memo[n] != -1) return memo[n]
memo[n] = fib(n - 1) + fib(n - 2)
return memo[n]
}
fun main() {
println(fib(10)) // 55
}
🔷 개념
🔷 예제: 1260원을 500/100/50/10원 동전으로 최소 개수로 바꾸기
fun main() {
var money = 1260
val coins = listOf(500, 100, 50, 10)
var count = 0
for (coin in coins) {
val use = money / coin
count += use
money %= coin
}
println("필요한 동전 개수: $count") // 6
}
✅ 3가지 비교 요약
| 알고리즘 | 개념 요약 | 코드 특징 | 언제 사용 |
|---|---|---|---|
| 백트래킹 | 조건을 만족하는 모든 경우를 찾되, 불필요한 탐색은 미리 중단 | DFS + used[] + 재귀 | 모든 조합/순열/조건 탐색 |
| DP | 중복되는 계산을 기억해서 빠르게 처리 | memo[] + 재귀 or 반복문 | 피보나치, 경로, 최솟값, 최대값 |
| Greedy | 지금 당장 가장 좋은 선택만 함 | 정렬 + 반복문 | 동전 문제, 회의실 배정 등 |
✅ 이분 탐색 개념 요약
fun binarySearch(arr: IntArray, target: Int): Int {
var left = 0
var right = arr.lastIndex
while (left <= right) {
val mid = (left + right) / 2
when {
arr[mid] == target -> return mid // 찾았다!
arr[mid] < target -> left = mid + 1
else -> right = mid - 1
}
}
return -1 // 못 찾음
}
fun main() {
val arr = intArrayOf(1, 3, 5, 7, 9, 11, 13)
val target = 7
val index = binarySearch(arr, target)
if (index != -1) {
println("값 $target 은 ${index}번 인덱스에 있습니다.")
} else {
println("값을 찾을 수 없습니다.")
}
}