시작하기
01 Array & ContiguousArray
02 Linked List
03 Stack / Queue
04 Deque
05 Binary Tree
06 BST
07 Heap / Priority Queue
08 Graph
09 Hash Table
10 Trie
📚 전체 학습 커리큘럼 (20개 챕터) 바로가기 →
개발자리 · Swift 교육 콘텐츠

Swift로 배우는
자료구조

"왜 이렇게 느려?" — 자료구조 하나 바꿨을 뿐인데 100배 빨라지는 경험.
10개 핵심 자료구조의 동작 원리를 시각화로 이해하고, Playground에서 직접 구현합니다.

추천 학습 순서
STEP 1 · 지금 여기
🎬 시각화로 이해
버튼 클릭으로 push·pop·insert가
메모리에서 어떻게 동작하는지
애니메이션으로 확인

자료구조 10개 · 실무 사용처 포함
STEP 2 · learn.html
📚 개념 + 코딩 테스트 추천
복잡도 분석·Swift 코드·체크리스트
알고리즘 6개(정렬·DP·Greedy 등)
진도 추적으로 완주

자료구조 14개 + 알고리즘 6개
→ 학습 시작
STEP 3 · Xcode
⌨️ 직접 구현
Playground 파일 다운로드 후
Xcode에서 빈 함수를 직접
채워가며 구현 연습

learn.html에서 다운로드
"왜 이렇게 느려?"
같은 기능인데 100배 느린 코드가 있다
유저 10만 명의 닉네임에서 중복을 찾는 코드입니다. 왼쪽은 Array, 오른쪽은 Set을 사용했습니다.
기능은 동일합니다. 하지만 걸리는 시간은 전혀 다릅니다.
Array — O(n²)
var seen: [String] = []
var duplicates: [String] = []

for name in allUsers {
  if seen.contains(name) { // O(n) 매번!
    duplicates.append(name)
  }
  seen.append(name)
}
Set (HashTable) — O(n)
var seen: Set<String> = []
var duplicates: [String] = []

for name in allUsers {
  if !seen.insert(name).inserted { // O(1)!
    duplicates.append(name)
  }
}
! Array: contains()가 매번 처음부터 끝까지 훑음 → 10만 × 10만 = 100억 번 비교 → 수십 초
> Set: 해시값으로 즉시 확인 → 10만 번만 비교 → 0.01초
자료구조를 바꾸면 코드가 아니라 차원이 바뀐다
자료구조는 "데이터를 어떤 모양으로 저장하느냐"를 결정합니다.
모양이 달라지면 같은 연산의 속도가 O(n)에서 O(1)로, O(n²)에서 O(n log n)으로 바뀝니다.
알고리즘을 아무리 최적화해도, 자료구조가 잘못되면 느릴 수밖에 없습니다.
잘못된 선택
// 정렬된 데이터에서 값 찾기
// Array + 선형탐색 → O(n)
array.contains(target)

// 맨 앞에 삽입이 잦은 경우
// Array → O(n) 매번 밀어야 함
array.insert(item, at: 0)

// 최솟값을 반복적으로 꺼내기
// 정렬 안 된 Array → O(n) 매번
array.min()
올바른 선택
// BST로 바꾸면 → O(log n)
bst.search(target)

// LinkedList → O(1)
linkedList.prepend(item)


// Heap → O(log n)
heap.removeMin()
자료구조를 배워서 어디다 써먹어요?

"개념은 알겠는데 실무에서 언제 쓰지?" — 각 자료구조가 실제로 쓰이는 구체적인 장면들입니다.
카드를 클릭하면 해당 챕터의 시각화로 이동합니다.

Array / ContiguousArray Ch.01
데이터 소스 & 고성능 수치 연산
Array — UITableView·CollectionView 데이터 소스, JSON 디코딩 등 일반적인 리스트 처리에 사용. ContiguousArray — Obj-C 브릿징 없이 연속 메모리가 보장되므로 오디오 버퍼, 이미지 픽셀 처리, ML 입력 텐서 등 대량 수치 연산에서 Array 대비 최대 2배 빠름.
let buffer: ContiguousArray<Float> = .init(repeating: 0, count: 44100)
LinkedList Ch.02
Undo / Redo 히스토리
텍스트 에디터, 그림판 등에서 작업 이력을 연결 리스트로 관리. 현재 위치에서 앞뒤 삽입/삭제가 O(1)이라 히스토리 조작이 가볍다. LRU 캐시 구현에도 필수.
cache.moveToFront(recentlyUsedNode)
Stack Ch.03
UINavigationController 화면 전환
push로 화면을 쌓고 pop으로 뒤로 가기. iOS 네비게이션 자체가 Stack 구조다. 괄호 짝 맞추기, JSON 파싱, 재귀 호출 스택도 전부 Stack.
navigationController.pushViewController(vc, animated: true)
Queue Ch.03
네트워크 요청 / 작업 스케줄링
API 요청을 순서대로 처리하는 OperationQueue. 이미지 다운로드 대기열, 푸시 알림 처리, 애니메이션 체이닝 모두 FIFO.
OperationQueue.main.addOperation { updateUI() }
Deque Ch.04
채팅 메시지 버퍼 / 슬라이딩 윈도우
새 메시지는 뒤에 추가, 오래된 메시지는 앞에서 제거 — 양쪽 O(1). 실시간 로그 뷰어, 최근 N개 이벤트 유지에 딱 맞는 구조.
messageBuffer.append(new); messageBuffer.removeFirst()
Binary Tree Ch.05
SwiftUI View 계층 구조
SwiftUI의 body는 트리 구조. VStack 안에 HStack, 그 안에 Text — 전부 트리. Auto Layout 제약 해석, 파일 시스템 탐색도 트리 순회.
VStack { HStack { Text("Hello") Image(...) } }
BST Ch.06
정렬된 데이터에서 빠른 검색
가격 필터(1만원~5만원), 날짜 범위 검색, 리더보드 순위 조회. 10만 개 데이터에서도 O(log n) = 약 17번 비교면 끝.
bst.rangeSearch(low: 10_000, high: 50_000)
Heap Ch.07
알림 우선순위 / 다익스트라 최단경로
긴급 알림을 먼저 보여주는 우선순위 큐. 지도 앱의 최단 경로 계산(Dijkstra), 작업 스케줄러의 핵심 엔진.
priorityQueue.enqueue(task, priority: .urgent)
Graph Ch.08
소셜 네트워크 / 지도 내비게이션
친구 추천(BFS), 경로 탐색(DFS/Dijkstra), 팔로우 관계 분석. 의존성 해결(SPM 패키지 그래프), 추천 시스템도 그래프 기반.
graph.shortestPath(from: "서울", to: "부산")
Hash Table Ch.09
Dictionary 캐싱 / 중복 제거
Swift의 Dictionary, Set이 내부적으로 해시 테이블. 이미지 캐시(URL→UIImage), 유저 세션 관리, API 응답 메모이제이션 전부 여기.
imageCache[url] = downloadedImage // O(1)
Trie Ch.10
검색창 자동완성 / 맞춤법 검사
"sw"만 쳐도 "swift", "switch", "swiftui"를 즉시 제안. 사전 앱, 연락처 검색, IP 라우팅 테이블, 입력 예측 전부 Trie.
trie.autocomplete(prefix: "sw") // ["swift", "swiftui"]
실습 진행 순서
1
시각화로 동작 확인
왼쪽 사이드바에서 챕터를 선택해 인터랙티브 시각화를 실행합니다.
버튼을 눌러 삽입·삭제·탐색이 메모리/트리/그래프 위에서 어떻게 동작하는지 직접 관찰합니다.
이 페이지
2
Playground에서 코드 직접 작성
DataStructures.playgroundbook를 Swift Playgrounds 앱으로 열고 해당 챕터 페이지로 이동합니다.
시각화에서 확인한 동작을 직접 구현하고 실행해봅니다.
DataStructures.playgroundbook Swift Playgrounds 앱 필요
3
다음 챕터로 반복
아래 챕터 목록에서 다음 챕터를 선택해 1번부터 반복합니다.
Ch01 → Ch02 → … → Ch10
챕터 목록
01
Array & ContiguousArray
CoW · 메모리 레이아웃
02
Linked List
노드 구조 · O(1) 삽입/삭제
03
Stack / Queue
LIFO · FIFO · 이중 Stack
04
Deque
링 버퍼 · 양방향 O(1)
05
Binary Tree
전위 · 중위 · 후위 · BFS
06
Binary Search Tree
삽입 · 삭제 · 탐색
07
Heap / Priority Queue
siftUp · siftDown
08
Graph
BFS · DFS · 인접리스트
09
Hash Table
충돌 해결 · Chaining
10
Trie
자동완성 · O(m) 탐색
무료 다운로드
🗂
치트시트 PDF
10개 DS 요약 · 7KB
📝
코딩테스트 문제집
50문제 + 풀이 · 15KB
💼
기술면접 질문집
100문제 + 답안 · 30KB
Chapter 01

Array vs ContiguousArray

CoW(Copy-on-Write) 동작과 메모리 레이아웃의 차이를 시각적으로 확인합니다.

시작 전 질문
01

Swift의 Array를 복사했을 때 메모리는 어떻게 되는가?

검색 → Swift Array Copy-on-Write mechanism
02

Array와 ContiguousArray는 언제 성능 차이가 나는가?

검색 → Swift ContiguousArray vs Array performance
03

배열 끝에 추가하는 append()는 왜 O(1)이라고 하는가?

검색 → amortized time complexity array append
⌨️ 시각화로 개념을 확인한 뒤, Ch01_Array Playground에서 직접 CoW와 성능 비교를 코딩해보세요. Playground 받기
개념

Swift의 Array는 값 타입이지만 내부적으로 힙 버퍼를 참조합니다. 복사를 해도 바로 메모리를 두 배로 쓰지 않고, 두 변수가 같은 버퍼를 공유합니다.

한쪽이 수정되는 순간 그때 비로소 버퍼를 복제합니다. 이를 CoW(Copy-on-Write)라고 합니다. 덕분에 불필요한 복사 비용을 줄일 수 있습니다.

ContiguousArray는 Obj-C 브릿징이 없어 항상 연속 메모리가 보장되고 성능이 더 예측 가능합니다.

사용법
  1. append() 를 눌러 배열 a에 원소를 추가하세요.
  2. b = a (복사) 를 누르면 b가 생깁니다. 아직 두 배열은 같은 메모리를 공유 중입니다.
  3. b.append() → CoW 발동 을 누르면 b가 수정되면서 버퍼가 실제로 복제되는 순간을 확인할 수 있습니다. 메모리 주소가 달라지는 것에 주목하세요.
메모리 레이아웃 — CoW 시뮬레이션
배열 a = [1, 2, 3] 에서 시작합니다. append → 복사 → CoW 순서로 눌러보세요.
시간복잡도
연산
복잡도
비고
subscript [i]
O(1)
연속 메모리 직접 접근
append()
O(1)*
amortized, 2배 재할당
insert(at:)
O(n)
이후 원소 전부 이동
remove(at:)
O(n)
이후 원소 전부 이동
contains()
O(n)
선형 탐색
생각해보기
Q1

var b = a 직후 b를 수정하지 않으면 메모리 사용량은 a 하나분인가 두 개분인가?

Q2

append()가 capacity를 넘어 재할당이 발생하면, 기존 데이터는 어디로 가는가?

Q3

ContiguousArray가 유리한 상황에서도 그냥 Array를 쓴다면 실제 성능 차이는 몇 %일까?

실전 문제
필수
10만 개의 Int를 Array와 ContiguousArray에 각각 추가하고 시간을 측정하세요
배열의 capacity가 2배씩 늘어나는 것을 직접 관찰하는 코드를 작성하세요
도전
insert(at: 0)과 append()의 성능을 1만 회 반복하여 비교하는 벤치마크를 만드세요
완료 체크리스트
실무 사용처
UITableView / CollectionView 데이터 소스
화면에 표시할 모델 배열을 Array로 관리. diffable data source와 함께 사용하면 UI 업데이트도 자동화됩니다.
var items: [Product] = []
JSON 디코딩 결과 저장
서버 API 응답을 Codable로 파싱한 결과를 배열에 저장. 대부분의 iOS 앱의 기본 데이터 흐름입니다.
let users = try decoder.decode([User].self, from: data)
오디오 버퍼 / vDSP 수치 연산
ContiguousArray는 연속 메모리가 보장되어 오디오 샘플, 이미지 픽셀, ML 텐서 등 대량 수치 연산에서 Array 대비 최대 2배 빠릅니다.
let buffer: ContiguousArray<Float> = .init(repeating: 0, count: 44100)
SwiftUI @State 배열
@State나 @Published로 배열을 선언하면 원소 변경 시 CoW가 발동되면서 SwiftUI가 변경을 감지해 뷰를 다시 그립니다.
@State private var todos: [TodoItem] = []
Chapter 02

Linked List

노드가 포인터로 연결되는 구조. prepend/removeFirst는 O(1), 인덱스 접근은 O(n).

시작 전 질문
01

배열의 insert(at: 0)은 왜 느린가? 이 문제를 해결하는 자료구조는?

검색 → Swift LinkedList vs Array insert performance
02

노드(Node)란 무엇이고 포인터는 어떤 역할을 하는가?

검색 → linked list node pointer concept
03

Swift에서 class와 struct의 차이가 연결 리스트 구현에 왜 중요한가?

검색 → Swift class reference type linked list
⌨️ 시각화로 개념을 확인한 뒤, Ch02_LinkedList Playground에서 Node 클래스와 LinkedList를 직접 구현해보세요. Playground 받기
개념

노드가 값(value)과 다음 노드의 주소(next)를 함께 갖습니다. 배열과 달리 메모리가 연속되지 않아도 됩니다.

맨 앞(head)에 삽입·삭제는 포인터 하나만 바꾸면 되어 O(1)입니다. 반면 특정 인덱스에 접근하려면 head부터 순서대로 따라가야 하므로 O(n)입니다.

배열은 인덱스 접근이 빠르고, 연결 리스트는 앞쪽 삽입·삭제가 빠릅니다. 상황에 맞게 선택합니다.

사용법
  1. 숫자를 입력하고 prepend() 를 누르면 맨 앞에 추가됩니다. 기존 노드들이 오른쪽으로 밀리는 게 아니라 head 포인터만 바뀌는 것에 주목하세요.
  2. append() 는 맨 뒤에 추가합니다. 끝 노드까지 순회해야 해서 리스트가 길수록 느려집니다.
  3. removeFirst() 로 head를 제거하면 다음 노드가 head가 됩니다.
단방향 연결 리스트 — 동적 삽입/삭제
초기 리스트: 10 → 20 → 30
생각해보기
Q1

단방향 연결 리스트에서 "이전 노드"로 돌아가려면 어떻게 해야 하는가?

Q2

연결 리스트의 중간에서 삭제가 O(1)이라고 하는데, 해당 노드를 찾는 데 O(n)이 걸리면 실제로는 O(n) 아닌가?

Q3

Swift의 Array가 이미 충분히 빠른데, 연결 리스트를 직접 구현해야 하는 실무 상황이 있는가?

실전 문제
필수
Node 클래스와 LinkedList 구조체를 만들고 prepend, append, removeFirst를 구현하세요
연결 리스트를 순회하며 모든 값을 출력하는 printAll() 메서드를 만드세요
도전
단방향 리스트에서 중간 노드를 한 번의 순회로 찾는 "러너(Runner) 기법"을 구현하세요
완료 체크리스트
실무 사용처
LRU Cache (최근 사용 캐시)
이미지 캐시에서 최근 사용한 항목을 앞으로 옮기고, 용량 초과 시 맨 뒤를 제거합니다. NSCache 내부도 유사한 구조입니다.
cache.moveToFront(recentlyUsedNode)
Undo / Redo 히스토리
텍스트 편집기에서 각 편집 액션을 노드로 저장. 이전/다음으로 이동하며 상태를 복원합니다.
history.current = history.current?.previous
음악 플레이어 재생 목록
이전곡/다음곡 이동이 잦은 재생 목록은 이중 연결 리스트로 O(1) 전환이 가능합니다.
player.currentTrack = player.currentTrack?.next
메모리 할당기 Free List
OS의 메모리 관리자가 사용 가능한 메모리 블록을 연결 리스트로 관리합니다. 할당/해제가 O(1)입니다.
freeList.prepend(deallocatedBlock)
Chapter 03

Stack / Queue

Stack은 LIFO, Queue는 FIFO. 이중 Stack으로 Queue를 amortized O(1)로 구현합니다.

시작 전 질문
01

iOS에서 "뒤로 가기"는 내부적으로 어떤 자료구조를 쓰는가?

검색 → UINavigationController stack push pop
02

대기열(Queue)은 왜 배열의 removeFirst()로 구현하면 비효율적인가?

검색 → Swift Queue implementation Array removeFirst O(n)
03

두 개의 Stack으로 Queue를 만들 수 있다는데, 어떻게 가능한가?

검색 → implement queue using two stacks
⌨️ 시각화로 개념을 확인한 뒤, Ch03_Stack Playground에서 Stack과 이중 Stack Queue를 직접 구현해보세요. Playground 받기
개념

Stack은 책을 쌓는 것처럼 마지막에 넣은 것이 먼저 나옵니다(LIFO). 함수 호출 스택, undo/redo, 괄호 검사 등에 활용됩니다.

Queue는 줄 서기처럼 먼저 넣은 것이 먼저 나옵니다(FIFO). 이 시각화는 이중 Stack으로 Queue를 구현합니다. inbox에 쌓고, dequeue 시 outbox가 비어있으면 inbox를 통째로 뒤집어 옮겨 amortized O(1)을 달성합니다.

사용법
  1. [Stack] 숫자를 입력하고 push() 를 여러 번 누르세요. 쌓이는 방향을 확인합니다.
  2. [Stack] pop() 을 누르면 가장 마지막에 쌓인 것부터 나옵니다.
  3. [Queue] enqueue() 를 여러 번 눌러 inbox에 원소를 채운 뒤 dequeue() 를 누르세요. outbox가 비어있을 때 inbox가 뒤집히는 순간을 관찰하세요.
Stack — LIFO (Last In, First Out)
💡 UINavigationController는 Stack입니다. push로 화면을 쌓고 pop으로 뒤로 가세요!
Queue — FIFO (이중 Stack 구현)
💡 알림 시스템 시뮬레이션. 알림을 쌓고 하나씩 dequeue해보세요!
생각해보기
Q1

Stack을 Array로 구현할 때 push/pop은 항상 O(1)인가? capacity를 넘기면 어떻게 되는가?

Q2

이중 Stack Queue에서 inbox→outbox 전환이 O(n)인데 왜 amortized O(1)이라고 하는가?

Q3

재귀 함수 호출도 Stack 구조라는데, 재귀가 너무 깊으면 어떤 문제가 발생하는가?

실전 문제
필수
Array 기반 Stack을 구현하고, 문자열의 괄호 짝이 맞는지 검사하는 함수를 만드세요
이중 Stack으로 Queue를 구현하세요 (enqueue, dequeue)
도전
undo/redo 기능을 두 개의 Stack으로 구현하세요
완료 체크리스트
실무 사용처
UINavigationController 화면 전환
push로 화면을 쌓고 pop으로 뒤로 갑니다. SwiftUI의 NavigationStack도 동일한 Stack 구조입니다.
navigationController.pushViewController(vc, animated: true)
괄호/태그 유효성 검사
HTML 태그, JSON 중괄호, 수식 괄호의 짝이 맞는지 Stack으로 O(n)에 검증합니다.
stack.push("("); if top == "(" { stack.pop() }
OperationQueue 작업 스케줄링
네트워크 요청, 이미지 다운로드를 Queue에 넣고 FIFO로 순서대로 처리합니다. GCD도 내부적으로 Queue입니다.
OperationQueue.main.addOperation { updateUI() }
푸시 알림 처리 대기열
도착한 푸시 알림을 Queue에 쌓고, 사용자가 확인하는 순서대로(FIFO) 하나씩 처리합니다.
notificationQueue.enqueue(notification)
Chapter 04

Deque

링 버퍼(Ring Buffer) 기반 양방향 큐. 앞/뒤 모두 O(1) 삽입/삭제.

시작 전 질문
01

Stack은 한쪽, Queue는 양쪽이지만 방향이 다르다. 양쪽 모두 삽입/삭제가 필요한 경우는?

검색 → Double-ended queue deque use cases
02

링 버퍼(Ring Buffer)란 무엇이고 왜 Deque 구현에 적합한가?

검색 → circular buffer ring buffer implementation
03

슬라이딩 윈도우 알고리즘에서 Deque는 어떤 역할을 하는가?

검색 → sliding window deque algorithm
⌨️ 시각화로 개념을 확인한 뒤, Ch04_Deque Playground에서 링 버퍼 기반 Deque를 직접 구현해보세요. Playground 받기
개념

Deque(Double-Ended Queue)는 앞과 뒤 양방향에서 모두 O(1)로 삽입·삭제할 수 있는 자료구조입니다.

링 버퍼(Ring Buffer)로 구현합니다. 고정 크기 배열을 원형으로 사용해, head와 tail 포인터가 배열 끝에 도달하면 다시 처음으로 돌아옵니다. 메모리 이동 없이 O(1)이 가능한 이유입니다.

앞/뒤로 자유롭게 넣고 빼야 하는 슬라이딩 윈도우, 양방향 탐색 등에 적합합니다.

사용법
  1. pushFront()pushBack() 을 번갈아 눌러보세요. 원형 배열의 head와 tail 포인터가 어느 방향으로 이동하는지 확인합니다.
  2. capacity(8)가 꽉 찰 때까지 계속 추가하면 어떻게 되는지 확인하세요.
  3. popFront()popBack() 으로 양쪽에서 삭제해보세요. 포인터가 반대 방향으로 이동합니다.
Ring Buffer 시각화
💡 채팅 메시지 버퍼 시뮬레이션. 양쪽 모두 O(1)로 삽입/삭제가 가능합니다.
생각해보기
Q1

링 버퍼가 꽉 찼을 때 원소를 더 추가하면 어떻게 처리해야 하는가?

Q2

일반 배열로 Deque를 구현하면 앞쪽 삽입/삭제가 O(n)인데, 링 버퍼로 O(1)이 되는 원리는?

Q3

Swift Collections의 Deque와 직접 구현한 링 버퍼 Deque의 차이는 무엇일까?

실전 문제
필수
링 버퍼 기반 Deque를 구현하세요 (pushFront, pushBack, popFront, popBack)
최대 5개만 유지하는 "최근 검색어" 저장소를 Deque로 구현하세요
도전
배열에서 크기 k인 슬라이딩 윈도우의 최댓값을 O(n)에 구하는 함수를 Deque로 구현하세요
완료 체크리스트
실무 사용처
채팅 메시지 버퍼
최근 N개 메시지만 유지하는 버퍼. 새 메시지는 뒤에 추가하고, 오래된 메시지는 앞에서 제거합니다.
messageBuffer.pushBack(new); messageBuffer.popFront()
브라우저 앞으로/뒤로 가기
방문 기록의 양쪽 끝에서 페이지를 추가/제거하여 앞으로/뒤로 탐색을 구현합니다.
backStack.pushBack(url); forwardStack.popBack()
슬라이딩 윈도우 최댓값
주식 차트에서 최근 K일간 최고가를 O(n)에 계산. Deque로 후보를 관리하며 양쪽에서 제거합니다.
deque.popBack(); deque.pushBack(newPrice)
작업 스케줄러 (양방향 우선순위)
긴급 작업은 앞에(pushFront), 일반 작업은 뒤에(pushBack) 추가하는 우선순위 큐로 활용합니다.
taskQueue.pushFront(urgentTask)
Chapter 05

Binary Tree

전위/중위/후위 순회를 애니메이션으로 확인합니다. 방문 순서가 핵심입니다.

시작 전 질문
01

트리 구조는 어디에 쓰이는가? 파일 시스템, SwiftUI View 계층 외에 또 있는가?

검색 → binary tree real world applications iOS
02

전위/중위/후위 순회의 차이가 실무에서 왜 중요한가?

검색 → tree traversal preorder inorder postorder use cases
03

BFS와 DFS는 각각 언제 선택하는가?

검색 → BFS vs DFS when to use
⌨️ 시각화로 개념을 확인한 뒤, Ch05_BinaryTree Playground에서 재귀 순회를 직접 구현해보세요. Playground 받기
개념

각 노드가 최대 2개의 자식(왼쪽, 오른쪽)을 가지는 트리입니다. 트리에 저장된 모든 노드를 방문하는 방법이 4가지 있습니다.

전위(Pre-order): 루트 → 왼쪽 → 오른쪽. 트리 복사에 유용합니다.
중위(In-order): 왼쪽 → 루트 → 오른쪽. BST에서 사용하면 정렬된 순서로 방문됩니다.
후위(Post-order): 왼쪽 → 오른쪽 → 루트. 자식 먼저 처리해야 할 때 씁니다.
레벨(BFS): 위층부터 가까운 노드 순서로 방문합니다.

사용법
  1. 순회 버튼을 클릭하면 노드가 방문 순서에 따라 하나씩 하이라이트됩니다. 아래 상태 바에 방문 순서가 표시됩니다.
  2. 전위 / 중위 / 후위 를 각각 눌러 결과 순서를 비교해보세요. 루트(1)가 언제 방문되는지 주목하세요.
  3. 레벨 순회(BFS) 는 나머지와 달리 깊이가 아닌 층(레벨) 기준으로 방문합니다.
트리 순회 애니메이션
💡 순회 방법을 선택하세요. SwiftUI View 계층, 파일 시스템 탐색 등에 쓰입니다.
생각해보기
Q1

중위 순회를 BST에 적용하면 정렬된 순서가 나오는 이유는?

Q2

후위 순회가 "자식을 먼저 처리"하는데, 이것이 필요한 실무 상황은? (힌트: 디렉토리 삭제)

Q3

트리의 높이(height)가 성능에 미치는 영향은?

실전 문제
필수
TreeNode 클래스를 만들고 전위/중위/후위 순회를 재귀로 구현하세요
BFS(레벨 순회)를 Queue를 사용해 구현하세요
도전
트리의 높이를 구하는 함수와, 좌우를 뒤집는(mirror) 함수를 구현하세요
완료 체크리스트
실무 사용처
SwiftUI View 계층 구조
VStack 안의 HStack 안의 Text... SwiftUI는 View를 트리로 구성하고 순회하며 렌더링합니다.
VStack { HStack { Text("Hello") Image(...) } }
파일 시스템 탐색
디렉토리 구조가 트리입니다. FileManager로 하위 파일을 순회할 때 DFS/BFS를 사용합니다.
FileManager.default.enumerator(at: rootURL)
JSON / XML 파싱
중첩된 JSON 구조는 트리입니다. 재귀적으로 순회하며 원하는 값을 추출합니다.
func parse(_ node: JSON) { node.children.forEach(parse) }
수식 계산 (Expression Tree)
"(3+5)*2"를 트리로 변환 후 후위 순회하면 올바른 계산 순서가 됩니다. 컴파일러 내부에서 사용합니다.
// * → [+, 2] → [3, 5] 후위: 3 5 + 2 *
Chapter 06

Binary Search Tree

삽입 시 불변 조건(왼쪽 < 루트 < 오른쪽)을 유지하며 트리가 자라는 과정을 봅니다.

시작 전 질문
01

정렬된 배열에서 이진 탐색은 O(log n)인데, 삽입은? BST가 이 문제를 어떻게 해결하는가?

검색 → binary search tree insert search O(log n)
02

BST가 한쪽으로 치우치면 왜 성능이 나빠지는가?

검색 → BST worst case skewed tree
03

균형 트리(AVL, Red-Black)는 어떤 원리로 편향을 방지하는가?

검색 → balanced BST AVL tree rotation
⌨️ 시각화로 개념을 확인한 뒤, Ch06_BST Playground에서 삽입·탐색 로직을 직접 구현해보세요. Playground 받기
개념

이진 탐색 트리(BST)는 왼쪽 자식 < 부모 < 오른쪽 자식 규칙을 항상 지킵니다. 이 덕분에 탐색 시 절반씩 범위를 줄여가며 O(log n)에 찾을 수 있습니다.

단, 삽입 순서가 나쁘면 한쪽으로 치우쳐(편향 트리) 최악 O(n)이 될 수 있습니다. 이를 막기 위해 AVL 트리, 레드-블랙 트리 같은 균형 트리를 사용합니다.

사용법
  1. 숫자를 입력하고 insert() 를 누르세요. 루트부터 크기를 비교하며 올바른 자리를 찾아 내려가는 과정을 봅니다.
  2. 큰 수만 계속 입력(예: 50, 60, 70, 80)하면 오른쪽으로만 치우친 편향 트리가 됩니다. 이때 탐색이 왜 느려지는지 생각해보세요.
  3. 숫자 입력 후 search() 를 누르면 루트에서부터 비교하며 좁혀가는 경로가 표시됩니다.
BST 삽입 / 탐색 시뮬레이션
💡 상품 가격 BST: [40, 20, 60, 10, 30]. 가격을 넣고 검색해보세요.
생각해보기
Q1

[1, 2, 3, 4, 5]를 순서대로 BST에 삽입하면 어떤 모양이 되는가? 이때 탐색 시간은?

Q2

BST의 삭제에서 "자식이 2개인 노드"를 삭제할 때 후속자(successor)를 쓰는 이유는?

Q3

Swift의 SortedSet이 내부적으로 어떤 트리를 쓸까?

실전 문제
필수
BST에 insert()와 contains()를 구현하세요
BST의 중위 순회 결과가 정렬된 배열인지 확인하세요
도전
BST에서 노드를 삭제하는 delete() 함수를 구현하세요 (리프, 자식1개, 자식2개 케이스)
완료 체크리스트
실무 사용처
상품 가격 범위 검색
쇼핑앱에서 "1만원~5만원" 필터를 BST의 범위 검색으로 O(log n + k)에 처리합니다.
bst.rangeSearch(low: 10_000, high: 50_000)
데이터베이스 인덱스 (B-Tree)
Core Data, SQLite의 인덱스는 BST를 확장한 B-Tree입니다. 수백만 건에서도 빠른 검색이 가능합니다.
@NSManaged var name: String // indexed
자동 정렬 컬렉션
삽입할 때마다 자동으로 정렬이 유지됩니다. 리더보드, 타임라인 등 정렬된 데이터에 적합합니다.
sortedScores.insert(newScore) // 자동 정렬
게임 랭킹 시스템
플레이어 점수를 BST에 저장하면 "내 순위가 몇 등인지"를 O(log n)에 계산할 수 있습니다.
let rank = bst.countNodesGreaterThan(myScore) + 1
Chapter 07

Heap / Priority Queue

Min-Heap: 루트가 항상 최솟값. siftUp/siftDown 과정을 시각화합니다.

시작 전 질문
01

배열에서 최솟값을 찾으면 O(n)인데, O(1)로 가져올 수 있는 자료구조는?

검색 → min heap priority queue O(1) minimum
02

Heap은 트리인데 왜 배열로 저장하는가?

검색 → heap array representation parent child index
03

iOS에서 우선순위가 있는 작업 관리(예: 네트워크 요청 우선순위)는 어떻게 구현하는가?

검색 → priority queue iOS task scheduling
⌨️ 시각화로 개념을 확인한 뒤, Ch07_Heap Playground에서 siftUp/siftDown을 직접 구현해보세요. Playground 받기
개념

Heap은 완전 이진 트리로, Min-Heap은 부모가 항상 자식보다 작거나 같습니다. 루트가 항상 최솟값이므로 O(1)로 최솟값을 꺼낼 수 있습니다.

삽입: 배열 끝에 추가 후 부모와 비교하며 올라갑니다(siftUp).
삭제: 루트 제거 후 마지막 원소를 루트로 올리고 자식과 비교하며 내려갑니다(siftDown).

트리 형태이지만 배열로 저장합니다. 인덱스 i의 부모는 (i-1)/2, 자식은 2i+1, 2i+2입니다.

사용법
  1. 숫자를 입력하고 insert() 를 누르세요. 트리 맨 끝에 추가된 뒤, 부모보다 작으면 자리를 바꾸며 올라가는(siftUp) 과정을 봅니다.
  2. remove() 를 누르면 루트(최솟값)가 제거됩니다. 마지막 노드가 루트로 올라온 뒤 아래로 내려가며(siftDown) 힙 조건을 복구하는 과정을 관찰하세요.
  3. 아래 배열 표현에서 트리와 배열이 어떻게 대응되는지 확인하세요.
Min-Heap 트리 ↔ 배열 표현
💡 우선순위 큐: LOW 3개로 시작. HIGH를 넣으면 맨 위로 올라가는 걸 확인하세요!
생각해보기
Q1

Min-Heap에서 두 번째로 작은 값은 반드시 루트의 자식에 있는가?

Q2

siftUp과 siftDown의 시간복잡도가 O(log n)인 이유는? (힌트: 트리의 높이)

Q3

Max-Heap으로 바꾸려면 비교 연산만 뒤집으면 되는가?

실전 문제
필수
배열 기반 MinHeap을 구현하세요 (insert, removeMin, siftUp, siftDown)
주어진 배열에서 K번째로 작은 원소를 Heap으로 찾으세요
도전
실행 중인 데이터 스트림에서 실시간 중앙값을 구하는 MedianFinder를 두 개의 Heap으로 구현하세요
완료 체크리스트
실무 사용처
네트워크 요청 우선순위
사용자가 보고 있는 화면의 이미지를 HIGH, 프리페치를 LOW로 설정해 중요한 요청을 먼저 처리합니다.
priorityQueue.enqueue(request, priority: .high)
다익스트라 최단 경로
네비게이션 앱에서 최단 경로를 구할 때 Min-Heap으로 가장 가까운 노드를 O(log n)에 꺼냅니다.
let nearest = minHeap.removeMin() // O(log n)
iOS 알림 우선순위 관리
결제 완료, 긴급 메시지 등 중요 알림을 먼저 표시하고, 마케팅 알림은 나중에 보여줍니다.
notifHeap.insert(Notification(priority: .urgent))
타이머/스케줄러
여러 타이머 중 가장 빨리 만료되는 것을 Min-Heap으로 관리. RunLoop의 타이머 스케줄링과 유사합니다.
let nextTimer = timerHeap.peek() // 가장 빠른 만료
Chapter 08

Graph

BFS와 DFS 탐색 경로를 애니메이션으로 비교합니다.

시작 전 질문
01

소셜 네트워크에서 "친구 추천"은 어떤 알고리즘으로 가능한가?

검색 → graph BFS friend recommendation algorithm
02

그래프를 코드로 표현하는 방법은 몇 가지가 있는가?

검색 → adjacency list vs adjacency matrix
03

네비게이션 앱의 최단 경로는 어떤 그래프 알고리즘을 쓰는가?

검색 → Dijkstra shortest path algorithm
⌨️ 시각화로 개념을 확인한 뒤, Ch08_Graph Playground에서 BFS·DFS를 직접 구현해보세요. Playground 받기
개념

Graph는 노드(정점, Vertex)와 노드를 잇는 간선(Edge)으로 구성됩니다. 트리와 달리 사이클이 있을 수 있고 부모-자식 관계가 없습니다.

BFS(너비 우선 탐색): 큐를 사용해 시작 노드에서 가까운 이웃부터 탐색합니다. 최단 경로 탐색에 적합합니다.
DFS(깊이 우선 탐색): 스택(또는 재귀)을 사용해 한 방향으로 끝까지 파고든 뒤 되돌아옵니다. 경로 탐색, 사이클 감지에 적합합니다.

사용법
  1. BFS 탐색 을 눌러보세요. 노드 A에서 출발해 거리 1인 이웃, 거리 2인 이웃 순서로 방문합니다. 가까운 곳부터 퍼져나가는 물결 형태입니다.
  2. DFS 탐색 을 눌러보세요. 한 경로를 끝까지 파고든 뒤 막히면 돌아와 다른 경로로 이동합니다.
  3. 두 방법의 방문 순서를 비교하며 어떤 상황에 어떤 탐색이 유리한지 생각해보세요.
그래프 BFS / DFS 탐색 시각화
💡 소셜 네트워크 시뮬레이션. BFS=친구추천(가까운순), DFS=경로탐색(깊이우선). 시작: A
생각해보기
Q1

BFS가 최단 경로를 보장하는 조건은? (힌트: 가중치)

Q2

DFS로 사이클을 감지하는 원리는?

Q3

인접 리스트와 인접 행렬 중 어떤 것이 메모리 효율적인가? (간선이 적을 때 vs 많을 때)

실전 문제
필수
인접 리스트로 그래프를 구현하고, BFS와 DFS를 각각 구현하세요
두 노드 사이의 경로가 존재하는지 BFS로 확인하세요
도전
가중치 없는 그래프에서 최단 경로와 그 거리를 구하는 함수를 구현하세요
완료 체크리스트
실무 사용처
소셜 네트워크 친구 추천
BFS로 2단계 이내 친구(친구의 친구)를 탐색해 "알 수도 있는 사람"을 추천합니다.
graph.bfs(from: me, maxDepth: 2)
지도 네비게이션 (최단 경로)
교차로를 노드, 도로를 간선으로 모델링. 다익스트라/A* 알고리즘으로 최단 경로를 계산합니다.
graph.shortestPath(from: "서울역", to: "강남역")
앱 의존성 분석
Swift Package의 의존성 관계를 그래프로 표현. 위상 정렬(DFS)로 빌드 순서를 결정합니다.
let buildOrder = dependencyGraph.topologicalSort()
추천 시스템
"이 상품을 본 사용자가 함께 본 상품"을 그래프 탐색으로 찾습니다. 협업 필터링의 기본 구조입니다.
graph.neighbors(of: viewedProduct)
Chapter 09

Hash Table

해시 충돌과 Separate Chaining을 직접 관찰합니다.

시작 전 질문
01

Swift의 Dictionary가 어떻게 O(1)로 값을 찾는가?

검색 → hash table dictionary O(1) lookup mechanism
02

서로 다른 키가 같은 위치에 매핑되면(충돌) 어떻게 처리하는가?

검색 → hash collision resolution chaining open addressing
03

Hashable 프로토콜은 왜 필요한가?

검색 → Swift Hashable protocol dictionary key
⌨️ 시각화로 개념을 확인한 뒤, Ch09_HashTable Playground에서 해시 함수와 충돌 해결을 직접 구현해보세요. Playground 받기
개념

Hash Table은 키(Key)를 해시 함수로 숫자(인덱스)로 변환해 배열에 저장합니다. 평균 O(1)로 삽입·탐색·삭제가 가능합니다. Swift의 Dictionary가 내부적으로 이 구조입니다.

서로 다른 키가 같은 인덱스로 매핑될 때 충돌(Collision)이 발생합니다. 이 시각화는 같은 버킷에 연결 리스트로 이어 붙이는 Separate Chaining으로 충돌을 해결합니다.

사용법
  1. 키와 값을 입력하고 insert(key, val) 을 눌러보세요. 키가 해시되어 어느 버킷에 저장되는지 확인합니다.
  2. 여러 키를 넣다 보면 같은 버킷에 두 개 이상 쌓이는 충돌이 발생합니다. 같은 버킷에 체인처럼 이어지는 것을 관찰하세요.
  3. 키를 입력하고 search(key) 를 누르면 해당 버킷을 찾아 체인을 순회하며 키를 찾는 과정을 확인합니다.
Hash Table — 충돌 시각화
💡 이미지 캐시 시뮬레이션. 같은 키를 두 번 insert해서 CACHE HIT를 체험해보세요!
생각해보기
Q1

해시 함수가 모든 키를 같은 버킷에 매핑하면 성능이 어떻게 되는가?

Q2

Load Factor가 높아지면 왜 성능이 나빠지는가? rehashing은 언제 하는가?

Q3

struct를 Dictionary 키로 쓰려면 무엇이 필요한가?

실전 문제
필수
간단한 HashTable을 구현하세요 (해시 함수, insert, search, Separate Chaining)
배열에서 두 수의 합이 target이 되는 쌍을 Dictionary로 O(n)에 찾으세요 (Two Sum)
도전
문자열에서 첫 번째 중복되지 않는 문자를 Dictionary로 찾는 함수를 구현하세요
완료 체크리스트
실무 사용처
이미지 캐시 (NSCache / Dictionary)
URL을 키로 다운로드한 이미지를 저장. 같은 이미지를 다시 요청하면 O(1)로 캐시에서 반환합니다.
imageCache[url] = downloadedImage // O(1)
Two Sum 문제 (코딩 테스트 필수)
배열에서 합이 target인 두 수를 Dictionary로 O(n)에 찾습니다. 면접 단골 문제입니다.
if let j = dict[target - nums[i]] { return [j, i] }
UserDefaults / Plist 저장
키-값 쌍으로 사용자 설정을 저장합니다. 내부적으로 Dictionary(해시 테이블)로 관리됩니다.
UserDefaults.standard.set(true, forKey: "isDarkMode")
중복 제거 (Set)
Swift의 Set은 내부적으로 해시 테이블입니다. 배열의 중복을 O(n)에 제거할 수 있습니다.
let unique = Array(Set(duplicatedArray))
Chapter 10

Trie

자동완성 탐색 경로가 트리 위에서 빛나며 강조됩니다.

시작 전 질문
01

10만 개의 단어에서 "app"으로 시작하는 단어를 모두 찾으려면?

검색 → trie autocomplete prefix search algorithm
02

Trie의 탐색이 단어 수와 무관하게 O(m)인 이유는?

검색 → trie time complexity word length
03

iOS 키보드 자동완성은 내부적으로 어떤 자료구조를 쓸까?

검색 → iOS keyboard autocomplete data structure
⌨️ 시각화로 개념을 확인한 뒤, Ch10_Trie Playground에서 자동완성 로직을 직접 구현해보세요. Playground 받기
개념

Trie(트라이)는 문자열을 문자 단위로 쪼개 트리에 저장합니다. 공통 접두어를 가진 단어들이 같은 경로를 공유하기 때문에 메모리를 절약합니다.

m글자 단어의 탐색이 O(m)으로, 단어 수와 무관합니다. 자동완성, 사전 검색, 오타 교정 등에 사용됩니다. iOS의 키보드 자동완성도 유사한 구조입니다.

사용법
  1. 입력란에 접두어(예: ap)를 입력하고 autocomplete(prefix) 를 누르세요. 해당 경로가 빛나며 그 아래에 있는 단어들이 표시됩니다.
  2. app, apple, apply가 루트~p~p까지 경로를 공유하는 것을 확인하세요. 이것이 Trie의 핵심입니다.
  3. 새 단어(예: apt)를 입력하고 insert(word) 를 누르면 기존 경로와 분기되는 지점에 새 노드가 추가됩니다.
Trie 자동완성 시뮬레이션
💡 검색창 자동완성 시뮬레이션. "ap"를 입력하고 autocomplete를 눌러보세요!
생각해보기
Q1

Trie에서 "apple"을 삭제할 때 "app"은 남겨야 한다면 어떻게 처리하는가?

Q2

HashMap으로도 자동완성을 구현할 수 있는데, Trie가 더 나은 이유는?

Q3

Trie의 메모리 사용량을 줄이는 방법은? (힌트: 압축 Trie)

실전 문제
필수
TrieNode와 Trie를 구현하세요 (insert, search, startsWith)
주어진 단어 목록을 Trie에 넣고 특정 접두어의 자동완성 결과를 반환하세요
도전
단어 삭제 기능을 구현하세요 (다른 단어의 경로를 훼손하지 않도록)
완료 체크리스트
실무 사용처
검색창 자동완성
사용자가 "sw"를 입력하면 Trie에서 접두어를 타고 "swift", "swiftui" 등 후보를 즉시 반환합니다.
trie.autocomplete(prefix: "sw") // ["swift", "swiftui"]
iOS 키보드 자동 교정
입력 중인 단어와 가장 유사한 단어를 Trie에서 찾아 교정 후보를 제안합니다.
trie.suggest(misspelled: "swft") // "swift"
IP 라우팅 테이블
네트워크 라우터가 IP 주소의 접두어를 Trie로 매칭해 패킷을 올바른 경로로 전달합니다.
router.longestPrefixMatch("192.168.1.100")
연락처 검색
전화번호부에서 이름 일부만 입력해도 일치하는 연락처를 즉시 필터링합니다. 한글 초성 검색도 Trie로 구현 가능합니다.
contactTrie.search(prefix: "김") // ["김철수", "김영희", ...]