Chapter 09
그래프
Graph
Part 1  그래프의 기본과 구현 · Graph Basics & Implementation
Part 2  깊이 우선 탐색 · Depth-First Search (DFS)
Part 3  그래프 응용 · Graph Applications
01
Part 1
그래프의 기본과 구현
Graph Basics & Implementation
그래프의 개념, 종류, 인접 행렬, 그리고 코드 구현을 학습합니다.

생활 속 그래프 구조

Graph Structures in Daily Life
한국어

우리 주변의 그래프

그래프는 여러 노드(정점)선(간선)으로 서로 연결된 자료구조입니다.

버스 노선도

버스 정류장(정점)이 노선(간선)으로 연결된 형태입니다. 여러 노선이 교차하는 복잡한 구조입니다.

SNS 관계망

링크드인, 페이스북 등에서 사람(정점)들이 친구 관계(간선)로 연결된 형태입니다.

도로/항공 지도

도시(정점)와 도시를 잇는 도로나 항공 노선(간선)이 그래프 구조입니다.

서울 부산 광주 대전 대구
English

Graphs Around Us

A graph is a data structure where multiple nodes (vertices) are connected by edges.

Bus Route Maps

Bus stops (vertices) connected by routes (edges). Multiple routes intersect creating complex structures.

Social Networks

On LinkedIn or Facebook, people (vertices) are connected by friendships (edges).

Road / Flight Maps

Cities (vertices) connected by roads or flight routes (edges) form graph structures.

Graph vs Tree

A tree is a special case of a graph — a connected graph with no cycles. Graphs can have cycles, disconnected parts, and directed edges!

그래프의 종류

Types of Graphs
한국어

세 가지 그래프 유형

무방향 그래프 (Undirected Graph)

간선에 방향이 없습니다. A→B로 갈 수 있으면 B→A로도 갈 수 있습니다.

A B C D G1: 무방향
방향 그래프 (Directed Graph)

간선에 방향이 있습니다. 화살표 방향으로만 이동 가능합니다.

A B C D G3: 방향
가중치 그래프 (Weighted Graph)

간선마다 가중치(비용, 거리 등)가 부여된 그래프입니다.

English

Three Types of Graphs

Undirected Graph

Edges have no direction. If A connects to B, then B also connects to A.

Directed Graph (Digraph)

Edges have direction (arrows). Movement only allowed in the arrow's direction.

Weighted Graph

Each edge carries a weight (cost, distance, etc.). Used for shortest path, minimum cost problems.

10 15 20 A B C Weighted Graph

Set Notation

G1 undirected: V={A,B,C,D}, E={(A,B),(A,C),(A,D),(B,C),(C,D)}
G3 directed: V={A,B,C,D}, E=<A,B>,<A,C>,<D,A>,<D,C>

인접 행렬 표현

Adjacency Matrix Representation
한국어

인접 행렬이란?

그래프를 코드로 구현할 때 인접 행렬(Adjacency Matrix)을 사용합니다. 정점이 N개이면 N×N 크기의 2차원 배열로 표현합니다.

무방향 그래프 G1의 인접 행렬

1출발점 A와 연결된 도착점 B, C, D의 칸을 1로 설정
2출발점 B와 연결된 도착점 A, C의 칸을 1로, 연결 안 된 D는 0
3같은 방식으로 C, D도 설정
ABCD
A0111
B1010
C1101
D1010

대칭!

무방향 그래프의 인접 행렬은 대각선을 기준으로 서로 대칭됩니다. A→B = 1이면 B→A도 반드시 1입니다.

English

What is an Adjacency Matrix?

An adjacency matrix represents a graph as an N×N 2D array, where N is the number of vertices.

Undirected Graph G1

1From vertex A, connected to B, C, D → set those cells to 1
2From vertex B, connected to A, C → set to 1; not connected to D → 0
3Repeat for C, D

Directed Graph G3

ABCD
A0110
B0000
C0000
D1010

Not Symmetric!

A directed graph's matrix is NOT symmetric. A→B=1 does not mean B→A=1. Only outgoing edges from each vertex are marked.

그래프 클래스와 구현

Graph Class & Implementation
한국어

Graph 클래스 (Code09-01)

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)]

무방향 그래프 G1 구현

G1 = Graph(4) G1.graph[0][1] = 1; G1.graph[0][2] = 1 G1.graph[0][3] = 1 G1.graph[1][0] = 1; G1.graph[1][2] = 1 G1.graph[2][0] = 1; G1.graph[2][1] = 1 G1.graph[2][3] = 1 G1.graph[3][0] = 1; G1.graph[3][2] = 1

방향 그래프 G3 구현

G3 = Graph(4) G3.graph[0][1] = 1; G3.graph[0][2] = 1 G3.graph[3][0] = 1; G3.graph[3][2] = 1

출력 결과

## G1 무방향 그래프 ##
0 1 1 1
1 0 1 0
1 1 0 1
1 0 1 0
## G3 방향 그래프 ##
0 1 1 0
0 0 0 0
0 0 0 0
1 0 1 0
English

Graph Class (Code09-01)

Graph Class Design

Graph(size) creates an size×size 2D array filled with 0. Each cell graph[i][j] represents the edge from vertex i to vertex j.

Undirected G1

For undirected graphs, always set both directions: graph[0][1]=1 AND graph[1][0]=1.

Directed G3

For directed graphs, only set the outgoing direction: A→B means graph[0][1]=1 only.

Output

## G1 Undirected ##
0 1 1 1    (A→B,C,D)
1 0 1 0    (B→A,C)
1 1 0 1    (C→A,B,D)
1 0 1 0    (D→A,C)
## G3 Directed ##
0 1 1 0    (A→B,C)
0 0 0 0    (B→none)
0 0 0 0    (C→none)
1 0 1 0    (D→A,C)

이름으로 그래프 구현

Named Graph Implementation
한국어

직관적인 변수 이름 사용 (Code09-02)

정점 번호를 이름으로 지정하면 더 직관적입니다.

nameAry = ['문별', '솔라', '휘인', '쯔위', '선미', '화사'] 문별, 솔라, 휘인, 쯔위, 선미, 화사 = 0,1,2,3,4,5 gSize = 6 G1 = Graph(gSize) G1.graph[문별][솔라] = 1 G1.graph[문별][휘인] = 1 G1.graph[솔라][문별] = 1 G1.graph[솔라][쯔위] = 1 # ... 나머지 연결
문별 솔라 휘인 쯔위 화사 선미 화사

printGraph 함수로 이쁘게 출력

def printGraph(g): print(' ', end=' ') for v in range(g.SIZE): print(nameAry[v], end=' ') print() for row in range(g.SIZE): print(nameAry[row], end=' ') for col in range(g.SIZE): print(g.graph[row][col], end=' ') print()
English

Named Variables (Code09-02)

Using names as variable indices makes code more readable.

Key Technique

문별, 솔라, 휘인, 쯔위, 선미, 화사 = 0,1,2,3,4,5
Now G1.graph[문별][솔라] = 1 is much clearer than G1.graph[0][1] = 1!

printGraph Function

Adds row and column headers using nameAry so the matrix is human-readable.

Output with Headers

   문별 솔라 휘인 쯔위 선미 화사
문별 0  1  1  0  0  0
솔라 1  0  0  1  0  0
휘인 1  0  0  1  0  0
쯔위 0  1  1  0  1  1
선미 0  0  0  1  0  1
화사 0  0  0  1  1  0

Python Trick

Python allows Korean variable names! 문별 = 0 is valid Python. This makes the graph code read like natural language.

연습문제 Part 1

Practice Part 1
한국어
연습문제 1-1 : 무방향/방향 그래프 구현

Graph 클래스로 4개 정점(A,B,C,D)의 무방향 그래프 G1과 방향 그래프 G3을 구현하고, 인접 행렬을 출력하시오. (Code09-01)

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = Graph(4) G1.graph[0][1]=1; G1.graph[0][2]=1; G1.graph[0][3]=1 G1.graph[1][0]=1; G1.graph[1][2]=1 G1.graph[2][0]=1; G1.graph[2][1]=1; G1.graph[2][3]=1 G1.graph[3][0]=1; G1.graph[3][2]=1 print('## G1 무방향 그래프 ##') for row in range(4): for col in range(4): print(G1.graph[row][col], end=' ') print() G3 = Graph(4) G3.graph[0][1]=1; G3.graph[0][2]=1 G3.graph[3][0]=1; G3.graph[3][2]=1 print('## G3 방향 그래프 ##') for row in range(4): for col in range(4): print(G3.graph[row][col], end=' ') print()
연습문제 1-2 : 무방향 그래프 (Self09-01)

4개 정점의 무방향 그래프를 구현하시오. 간선: A-D, B-C, B-D, C-B, D-A, D-B

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = Graph(4) G1.graph[0][3] = 1 G1.graph[1][2] = 1; G1.graph[1][3] = 1 G1.graph[2][1] = 1 G1.graph[3][0] = 1; G1.graph[3][1] = 1 print('## 무방향 그래프 ##') for row in range(4): for col in range(4): print(G1.graph[row][col], end=' ') print()
English
Practice 1-1 : Undirected/Directed Graph

Implement undirected graph G1 and directed graph G3 with 4 vertices (A,B,C,D) using Graph class. Print the adjacency matrices. (Code09-01)

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = Graph(4) G1.graph[0][1]=1; G1.graph[0][2]=1; G1.graph[0][3]=1 G1.graph[1][0]=1; G1.graph[1][2]=1 G1.graph[2][0]=1; G1.graph[2][1]=1; G1.graph[2][3]=1 G1.graph[3][0]=1; G1.graph[3][2]=1 print('## G1 Undirected ##') for row in range(4): for col in range(4): print(G1.graph[row][col], end=' ') print() G3 = Graph(4) G3.graph[0][1]=1; G3.graph[0][2]=1 G3.graph[3][0]=1; G3.graph[3][2]=1 print('## G3 Directed ##') for row in range(4): for col in range(4): print(G3.graph[row][col], end=' ') print()
Practice 1-2 : Undirected Graph (Self09-01)

Implement an undirected graph with 4 vertices. Edges: A-D, B-C, B-D, C-B, D-A, D-B

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = Graph(4) G1.graph[0][3] = 1 G1.graph[1][2] = 1; G1.graph[1][3] = 1 G1.graph[2][1] = 1 G1.graph[3][0] = 1; G1.graph[3][1] = 1 print('## Undirected Graph ##') for row in range(4): for col in range(4): print(G1.graph[row][col], end=' ') print()
02
Part 2
깊이 우선 탐색
Depth-First Search (DFS)
스택과 방문 배열을 이용하여 그래프의 모든 정점을 깊이 우선으로 탐색합니다.

깊이 우선 탐색(DFS) 개념

DFS Concept
한국어

깊이 우선 탐색이란?

그래프의 모든 정점을 방문하는 방법 중 하나로, 한 방향으로 갈 수 있을 때까지 깊이 들어간 후, 더 이상 갈 곳이 없으면 되돌아와서 다른 방향으로 탐색합니다.

필요한 자료구조

스택(stack) : 현재 경로를 기억하여 되돌아갈 때 사용

방문 배열(visitedAry) : 이미 방문한 정점을 기록하여 중복 방문 방지

1시작 정점을 스택에 넣고 방문 기록
2현재 정점에서 연결된 정점 중 방문하지 않은 첫 번째 정점으로 이동
3이동한 정점을 스택에 넣고 방문 기록
4더 이상 갈 곳이 없으면 스택에서 pop하여 되돌아감
5스택이 빌 때까지 2~4 반복

핵심 포인트

DFS는 미로 찾기와 같습니다. 한 길로 끝까지 가보고, 막히면 갈림길로 돌아와서 다른 길로 가는 방식입니다!

English

What is DFS?

DFS visits all vertices in a graph by going as deep as possible in one direction, then backtracking when there's nowhere else to go.

Required Data Structures

Stack : remembers the current path for backtracking

Visited Array (visitedAry) : records visited vertices to avoid revisiting

1Push start vertex onto stack & mark visited
2From current vertex, move to the first unvisited neighbor
3Push the new vertex onto stack & mark visited
4If no unvisited neighbor, pop from stack (backtrack)
5Repeat 2~4 until stack is empty

Key Insight

DFS is like solving a maze: go down one path until you hit a dead end, then backtrack to the last fork and try another path!

DFS 동작 과정 (단계별)

DFS Step-by-Step Walkthrough
한국어

4개 정점 그래프에서 DFS

시작 정점: A(0) / 인접 행렬 기준으로 탐색

A B C D 간선 정보: A-C, A-D B-C C-A, C-B, C-D D-A, D-C
1A 방문 → stack=[A], visited=[A]
A의 이웃: C(연결), D(연결) → 첫 번째 미방문 C로 이동
2C 방문 → stack=[A,C], visited=[A,C]
C의 이웃: A(방문함), B(미방문) → B로 이동
3B 방문 → stack=[A,C,B], visited=[A,C,B]
B의 이웃: C(방문함) → 미방문 없음! → pop(B)
4C로 복귀 → stack=[A,C]
C의 이웃 중 미방문: D → D로 이동
5D 방문 → stack=[A,C,D], visited=[A,C,B,D]
D의 이웃: A(방문), C(방문) → pop 반복 → 스택 비어짐!

최종 방문 순서

A → C → B → D

English

DFS on a 4-Vertex Graph

Start vertex: A(0) / Scan adjacency matrix row by row

Stack changes during DFS: A push A A C push C A C B push B → pop B (dead end) A C D push D → pop all (done)
1Visit A → stack=[A], visited=[A]
A's neighbors: C, D → first unvisited is C
2Visit C → stack=[A,C], visited=[A,C]
C's neighbors: A(visited), B(unvisited) → go to B
3Visit B → stack=[A,C,B], visited=[A,C,B]
B's neighbors: C(visited) → dead end → pop(B)
4Back at C → stack=[A,C]
C's unvisited: D → go to D
5Visit D → stack=[A,C,D], visited=[A,C,B,D]
D's neighbors: A,C (all visited) → pop until empty

Final Visit Order

A → C → B → D

DFS 코드 구현

DFS Implementation (Code09-03_)
한국어

DFS 코드 (Code09-03_)

그래프 생성 후, 스택과 방문 배열을 이용한 DFS 탐색

## 전역 변수 선언 부분 ## G1 = None stack = [] visitedAry = [] # 방문한 정점 ## 메인 코드 부분 ## G1 = Graph(4) G1.graph[0][2]=1; G1.graph[0][3]=1 G1.graph[1][2]=1 G1.graph[2][0]=1; G1.graph[2][1]=1 G1.graph[2][3]=1 G1.graph[3][0]=1; G1.graph[3][2]=1 current = 0 # 시작 정점 A stack.append(current) visitedAry.append(current)

핵심: while 루프

while (len(stack) != 0): next = None for vertex in range(4): if G1.graph[current][vertex]==1: if vertex in visitedAry: pass # 방문함→탈락 else: next = vertex break # 미방문→선택! if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop()

출력

방문 순서 -->A C B D

English

DFS Code (Code09-03_)

Build graph, then traverse using stack + visited array

Algorithm Logic

for loop: scan row current in adjacency matrix → find first unvisited neighbor
if next != None: move forward (push)
else: backtrack (pop)

# Print visit order print('방문 순서 -->', end='') for i in visitedAry: print(chr(ord('A')+i), end=' ')
chr() and ord() Trick

chr(ord('A')+0) = 'A'
chr(ord('A')+1) = 'B'
chr(ord('A')+2) = 'C'
Converts index 0,1,2,3 → 'A','B','C','D'

DFS vs BFS

DFS uses a stack (LIFO) → goes deep first.
BFS uses a queue (FIFO) → goes wide first.
Same graph, different visit orders!

정점 연결 확인 함수

findVertex Function (Code09-04)
한국어

findVertex() — 연결 확인

DFS를 이용하면 두 정점이 연결되어 있는지 확인할 수 있습니다. 시작 정점(0번)에서 DFS를 수행한 후, 찾는 정점이 방문 기록에 있으면 연결된 것입니다.

def findVertex(g, findVtx): stack = [] visitedAry = [] current = 0 # 시작 정점 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if g.graph[current][vertex]!=0: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() if findVtx in visitedAry: return True else: return False

핵심 변경점

Code09-03_의 DFS와 거의 동일하지만, 마지막에 findVtx in visitedAry를 확인하여 True/False를 반환합니다. 이 함수는 Part 3의 최소 신장 트리에서도 사용됩니다!

English

findVertex() — Connectivity Check

Using DFS, we can check if two vertices are connected. Run DFS from vertex 0, then check if the target vertex appears in the visited list.

How It Works

1. Run full DFS from vertex 0
2. After DFS completes, check: findVtx in visitedAry
3. If True → the vertex is reachable (connected)
4. If False → the vertex is NOT reachable (disconnected)

Key Differences from Code09-03_

• Wrapped in a function (reusable)
• Checks != 0 instead of == 1 (works with weighted graphs too!)
• Returns True/False instead of printing

findVertex(graph, targetVertex) DFS from vertex 0 True or False

Used in Part 3!

This function is essential for Kruskal's algorithm — it checks if removing an edge disconnects the graph.

연습문제 Part 2

Practice Part 2
한국어
연습문제 2-1 : DFS 탐색 (Code09-03_)

4개 정점(A,B,C,D)의 무방향 그래프를 생성하고, 정점 A에서 DFS 탐색을 수행하여 방문 순서를 출력하시오.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = None stack = [] visitedAry = [] G1 = Graph(4) G1.graph[0][2]=1; G1.graph[0][3]=1 G1.graph[1][2]=1 G1.graph[2][0]=1; G1.graph[2][1]=1 G1.graph[2][3]=1 G1.graph[3][0]=1; G1.graph[3][2]=1 print('## G1 무방향 그래프 ##') for row in range(4): for col in range(4): print(G1.graph[row][col], end=' ') print() current = 0 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(4): if G1.graph[current][vertex] == 1: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() print('방문 순서 -->', end='') for i in visitedAry: print(chr(ord('A')+i), end=' ')
연습문제 2-2 : 이름 그래프 DFS (Self09-02)

6명(문별,솔라,휘인,쯔위,선미,화사)의 친구 관계 그래프에서 DFS 탐색을 수행하여 방문 순서를 이름으로 출력하시오.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = None stack = [] visitedAry = [] nameAry = ['문별','솔라','휘인','쯔위','선미','화사'] 문별,솔라,휘인,쯔위,선미,화사 = 0,1,2,3,4,5 gSize = 6 G1 = Graph(gSize) G1.graph[문별][솔라]=1; G1.graph[문별][휘인]=1 G1.graph[솔라][문별]=1; G1.graph[솔라][쯔위]=1 G1.graph[휘인][문별]=1; G1.graph[휘인][쯔위]=1 G1.graph[쯔위][솔라]=1; G1.graph[쯔위][휘인]=1 G1.graph[쯔위][선미]=1; G1.graph[쯔위][화사]=1 G1.graph[선미][쯔위]=1; G1.graph[선미][화사]=1 G1.graph[화사][쯔위]=1; G1.graph[화사][선미]=1 current = 0 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if G1.graph[current][vertex] == 1: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() print('방문 순서 -->', end='') for i in visitedAry: print(nameAry[i], end=' ')
English
Practice 2-1 : DFS Traversal (Code09-03_)

Create an undirected graph with 4 vertices (A,B,C,D), perform DFS from vertex A, and print the visit order.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = None stack = [] visitedAry = [] G1 = Graph(4) G1.graph[0][2]=1; G1.graph[0][3]=1 G1.graph[1][2]=1 G1.graph[2][0]=1; G1.graph[2][1]=1 G1.graph[2][3]=1 G1.graph[3][0]=1; G1.graph[3][2]=1 print('## G1 Undirected Graph ##') for row in range(4): for col in range(4): print(G1.graph[row][col], end=' ') print() current = 0 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(4): if G1.graph[current][vertex] == 1: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() print('방문 순서 -->', end='') for i in visitedAry: print(chr(ord('A')+i), end=' ')
Practice 2-2 : Named Graph DFS (Self09-02)

Perform DFS on a 6-person friend graph (문별,솔라,휘인,쯔위,선미,화사) and print the visit order using names.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = None stack = [] visitedAry = [] nameAry = ['문별','솔라','휘인','쯔위','선미','화사'] 문별,솔라,휘인,쯔위,선미,화사 = 0,1,2,3,4,5 gSize = 6 G1 = Graph(gSize) G1.graph[문별][솔라]=1; G1.graph[문별][휘인]=1 G1.graph[솔라][문별]=1; G1.graph[솔라][쯔위]=1 G1.graph[휘인][문별]=1; G1.graph[휘인][쯔위]=1 G1.graph[쯔위][솔라]=1; G1.graph[쯔위][휘인]=1 G1.graph[쯔위][선미]=1; G1.graph[쯔위][화사]=1 G1.graph[선미][쯔위]=1; G1.graph[선미][화사]=1 G1.graph[화사][쯔위]=1; G1.graph[화사][선미]=1 current = 0 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if G1.graph[current][vertex] == 1: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() print('방문 순서 -->', end='') for i in visitedAry: print(nameAry[i], end=' ')
03
Part 3
그래프 응용
Graph Applications
최소 신장 트리(MST)와 크루스칼 알고리즘, 실생활 응용을 학습합니다.

최소 신장 트리 (MST)

Minimum Spanning Tree
한국어

최소 신장 트리란?

신장 트리(Spanning Tree): 모든 정점을 포함하면서 사이클이 없는 부분 그래프입니다. 정점이 N개이면 간선은 정확히 N-1개입니다.

최소 신장 트리(MST): 간선의 가중치 합이 최소인 신장 트리입니다.

실생활 예시

6개 도시를 자전거 도로로 연결할 때, 모든 도시를 연결하면서 건설 비용(가중치)을 최소화하려면 MST를 구하면 됩니다!

10 11 12 20 25 춘천 속초 서울 대전 광주 부산 MST: 가중치 합 최소!
English

What is MST?

Spanning Tree: A subgraph that includes ALL vertices with NO cycles. With N vertices, it has exactly N-1 edges.

Minimum Spanning Tree (MST): The spanning tree with the minimum total edge weight.

Real-World Example

Connect 6 cities with bicycle roads at minimum cost. All cities must be reachable, but we want the cheapest total construction!

MST Properties

• N vertices → exactly N-1 edges
• All vertices are connected (reachable from any vertex)
No cycles (removing any edge disconnects)
• Total weight is minimized

Key Formula

If graph has 6 vertices → MST has exactly 5 edges (6-1=5).
Start with all edges, keep removing the most expensive ones while ensuring connectivity!

크루스칼 알고리즘

Kruskal's Algorithm
한국어

크루스칼 알고리즘 동작

가중치가 가장 큰 간선부터 제거하되, 그래프가 분리되지 않는 경우에만 제거합니다.

1모든 간선을 가중치 내림차순으로 정렬
2가장 큰 가중치의 간선을 임시 제거
3findVertex()로 양쪽 정점이 여전히 연결되는지 확인
4연결됨 → 완전 삭제 / 연결 안 됨 → 복구
5간선 수가 N-1개가 될 때까지 반복

핵심 아이디어

비싼 도로부터 하나씩 없애보기! 없애도 모든 도시가 연결되면 없애고, 끊어지면 다시 살리기!

English

How Kruskal's Works

Remove edges from highest weight first, but only if removing doesn't disconnect the graph.

1Sort all edges by weight in descending order
2Temporarily remove the highest-weight edge
3Use findVertex() to check if both endpoints are still connected
4Still connected → delete permanently / Disconnected → restore
5Repeat until only N-1 edges remain

Code Pattern

1. edgeAry = list of [weight, start, end]
2. Sort descending by weight
3. Remove duplicates (undirected → each edge listed twice)
4. While len(newAry) > gSize-1: try removing

크루스칼 알고리즘 단계별

Kruskal Step-by-Step
한국어

자전거 도로 예제 (Code09-05)

6개 도시, 8개 간선의 가중치 그래프

간선가중치결과
서울↔광주50제거해도 연결됨 → 삭제!
서울↔속초40제거해도 연결됨 → 삭제!
대전↔부산30제거해도 연결됨 → 삭제!
광주↔부산25제거하면 부산 끊어짐 → 복구!
대전↔광주20제거하면 끊어짐 → 복구!

최종 MST (5개 간선)

춘천↔서울(10), 서울↔대전(11), 속초↔대전(12), 대전↔광주(20), 광주↔부산(25)

총 비용 = 10+11+12+20+25 = 78

English

Bicycle Road Example (Code09-05)

6 cities, 8 edges weighted graph

10 11 12 20 25 춘천 속초 서울 대전 광주 부산 MST: Total = 78
Edge Removal Summary

50 (서울↔광주) → removed
40 (서울↔속초) → removed
30 (대전↔부산) → removed
25 (광주↔부산) → kept! (would disconnect)
20 (대전↔광주) → kept! (would disconnect)

크루스칼 코드 구현

Kruskal Implementation (Code09-05)
한국어

Code09-05 핵심 코드

간선 목록 생성 및 정렬

edgeAry = [] for i in range(gSize): for k in range(gSize): if G1.graph[i][k] != 0: edgeAry.append( [G1.graph[i][k], i, k]) from operator import itemgetter edgeAry = sorted(edgeAry, key=itemgetter(0), reverse=True) # 중복 제거 (무방향→양방향 중복) newAry = [] for i in range(0,len(edgeAry),2): newAry.append(edgeAry[i])

간선 제거 루프

index = 0 while (len(newAry)>gSize-1): start = newAry[index][1] end = newAry[index][2] saveCost = newAry[index][0] G1.graph[start][end] = 0 G1.graph[end][start] = 0 startYN = findVertex(G1, start) endYN = findVertex(G1, end) if startYN and endYN: del(newAry[index]) else: G1.graph[start][end]=saveCost G1.graph[end][start]=saveCost index += 1
English

Code09-05 Key Code

Edge List Creation

Step 1: Build Edge List

Scan adjacency matrix → collect [weight, start, end] for each non-zero cell.

Step 2: Sort Descending

sorted(..., reverse=True) puts the heaviest edge first so we try removing it.

Step 3: Remove Duplicates

Undirected graphs list each edge twice (A→B and B→A). Taking every 2nd entry gives unique edges.

Edge Removal Loop

Logic per edge:

1. Temporarily remove edge (set to 0)
2. Check findVertex(G1, start) and findVertex(G1, end)
3. Both True → graph still connected → delete from list
4. Either False → graph disconnected → restore the edge, move to next

Loop Condition

len(newAry) > gSize-1
We stop when exactly N-1 edges remain — that's the MST!

응용: 편의점 허니버터칩 탐색

Application: Convenience Store Search (Ex09-01)
한국어

편의점 네트워크에서 최대 재고 찾기

5개 편의점이 그래프로 연결되어 있고, 각 편의점에 허니버터칩 재고가 있습니다. DFS로 모든 편의점을 방문하며 가장 많은 재고를 보유한 편의점을 찾습니다.

GS2530 CU60 Seven1110 MiniStop90 Emart2440
storeAry = [ ['GS25',30], ['CU',60], ['Seven11',10], ['MiniStop',90], ['Emart24',40]] # DFS 중 최대값 추적 if storeAry[current][1] > maxCount: maxCount = storeAry[current][1] maxStore = current

출력

허니버터칩 최대 보유 편의점(개수) --> MiniStop ( 90 )

English

Find Max Inventory in Store Network

5 stores connected as a graph, each with honey butter chip inventory. Use DFS to visit all stores and find the one with the most inventory.

Key Addition to DFS

Standard DFS + max tracking:
At each new visit, compare storeAry[current][1] with maxCount. Update if larger.

Data Structure: 2D Array

storeAry[i][0] = store name
storeAry[i][1] = chip count
This is a common pattern: parallel data alongside the graph!

DFS Visit Order

GS25(30) → CU(60) → Seven11(10) → MiniStop(90) → Emart24(40)

Winner: MiniStop with 90 chips!

응용: 해저 케이블 MST

Application: Undersea Cable MST (Ex09-02)
한국어

해저 케이블 최소 비용 연결

6개 도시(서울, 뉴욕, 런던, 북경, 방콕, 파리)를 해저 케이블로 연결할 때, 최소 비용으로 모든 도시를 연결합니다.

10 50 40 30 20 서울 북경 뉴욕 런던 방콕 파리

Code09-05와 동일한 패턴!

도시 이름과 간선 가중치만 바꾸면 동일한 크루스칼 알고리즘을 적용할 수 있습니다.

English

Undersea Cable Minimum Cost

Connect 6 cities (서울, 뉴욕, 런던, 북경, 방콕, 파리) with undersea cables at minimum total cost.

Same Pattern as Code09-05!

1. Build weighted graph with city distances
2. Create edge list, sort descending
3. Remove duplicates
4. Remove heaviest edges while connected
5. Result: MST with 5 edges

Key Difference: reverse=False

Ex09-02 sorts reverse=False (ascending) in the code, but the removal logic stays the same — it still processes from the end of the sorted list to remove heavy edges first. The approach works either way!

MST Result

서울↔북경(10), 방콕↔파리(20), 런던↔방콕(30), 북경↔뉴욕(40), 북경↔방콕(50)

Total = 150

연습문제 Part 3

Practice Part 3
한국어
연습문제 3-1 : 편의점 허니버터칩 (Ex09-01)

5개 편의점 네트워크에서 DFS로 모든 편의점을 방문하여 허니버터칩을 가장 많이 보유한 편의점을 찾으시오.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] def printGraph(g): print('\t', end='') for v in range(g.SIZE): print("%9s" % storeAry[v][0], end=' ') print() for row in range(g.SIZE): print("%9s" % storeAry[row][0], end=' ') for col in range(g.SIZE): print("%8d" % g.graph[row][col], end=' ') print() print() G1 = None storeAry = [['GS25',30],['CU',60],['Seven11',10], ['MiniStop',90],['Emart24',40]] GS25,CU,Seven11,MiniStop,Emart24 = 0,1,2,3,4 gSize = 5 G1 = Graph(gSize) G1.graph[GS25][CU]=1; G1.graph[GS25][Seven11]=1 G1.graph[CU][GS25]=1; G1.graph[CU][Seven11]=1 G1.graph[CU][MiniStop]=1 G1.graph[Seven11][GS25]=1; G1.graph[Seven11][CU]=1 G1.graph[Seven11][MiniStop]=1 G1.graph[MiniStop][Seven11]=1 G1.graph[MiniStop][CU]=1 G1.graph[MiniStop][Emart24]=1 G1.graph[Emart24][MiniStop]=1 print('## 편의점 그래프 ##') printGraph(G1) stack = [] visitedAry = [] current = 0 maxStore = current maxCount = storeAry[current][1] stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if G1.graph[current][vertex] == 1: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) if storeAry[current][1] > maxCount: maxCount = storeAry[current][1] maxStore = current else: current = stack.pop() print('허니버터칩 최대 보유 편의점(개수) -->', storeAry[maxStore][0], '(', storeAry[maxStore][1], ')')
연습문제 3-2 : 해저 케이블 MST (Ex09-02)

6개 도시를 해저 케이블로 연결하는 최소 비용 연결도를 크루스칼 알고리즘으로 구하시오.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] def printGraph(g): print(' ', end=' ') for v in range(g.SIZE): print(cityAry[v], end=' ') print() for row in range(g.SIZE): print(cityAry[row], end=' ') for col in range(g.SIZE): print("%2d" % g.graph[row][col], end=' ') print() print() def findVertex(g, findVtx): stack = [] visitedAry = [] current = 0 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if g.graph[current][vertex] != 0: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() if findVtx in visitedAry: return True else: return False G1 = None cityAry = ['서울','뉴욕','런던','북경','방콕','파리'] 서울,뉴욕,런던,북경,방콕,파리 = 0,1,2,3,4,5 gSize = 6 G1 = Graph(gSize) G1.graph[서울][뉴욕]=80; G1.graph[서울][북경]=10 G1.graph[뉴욕][서울]=80; G1.graph[뉴욕][북경]=40 G1.graph[뉴욕][방콕]=70 G1.graph[런던][방콕]=30; G1.graph[런던][파리]=60 G1.graph[북경][서울]=10; G1.graph[북경][뉴욕]=40 G1.graph[북경][방콕]=50 G1.graph[방콕][뉴욕]=70; G1.graph[방콕][북경]=50 G1.graph[방콕][런던]=30; G1.graph[방콕][파리]=20 G1.graph[파리][방콕]=20; G1.graph[파리][런던]=60 print('## 해저 케이블 전체 연결도 ##') printGraph(G1) edgeAry = [] for i in range(gSize): for k in range(gSize): if G1.graph[i][k] != 0: edgeAry.append([G1.graph[i][k], i, k]) from operator import itemgetter edgeAry = sorted(edgeAry, key=itemgetter(0), reverse=False) newAry = [] for i in range(0,len(edgeAry),2): newAry.append(edgeAry[i]) index = 0 while (len(newAry) > gSize-1): start = newAry[index][1] end = newAry[index][2] saveCost = newAry[index][0] G1.graph[start][end] = 0 G1.graph[end][start] = 0 startYN = findVertex(G1, start) endYN = findVertex(G1, end) if startYN and endYN: del(newAry[index]) else: G1.graph[start][end] = saveCost G1.graph[end][start] = saveCost index += 1 print('## 가장 효율적인 해저 케이블 연결도 ##') printGraph(G1)
English
Practice 3-1 : Convenience Store Search (Ex09-01)

Use DFS on a 5-store network to find the store with the most honey butter chips.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] def printGraph(g): print('\t', end='') for v in range(g.SIZE): print("%9s" % storeAry[v][0], end=' ') print() for row in range(g.SIZE): print("%9s" % storeAry[row][0], end=' ') for col in range(g.SIZE): print("%8d" % g.graph[row][col], end=' ') print() print() G1 = None storeAry = [['GS25',30],['CU',60],['Seven11',10], ['MiniStop',90],['Emart24',40]] GS25,CU,Seven11,MiniStop,Emart24 = 0,1,2,3,4 gSize = 5 G1 = Graph(gSize) G1.graph[GS25][CU]=1; G1.graph[GS25][Seven11]=1 G1.graph[CU][GS25]=1; G1.graph[CU][Seven11]=1 G1.graph[CU][MiniStop]=1 G1.graph[Seven11][GS25]=1; G1.graph[Seven11][CU]=1 G1.graph[Seven11][MiniStop]=1 G1.graph[MiniStop][Seven11]=1 G1.graph[MiniStop][CU]=1 G1.graph[MiniStop][Emart24]=1 G1.graph[Emart24][MiniStop]=1 print('## Store Graph ##') printGraph(G1) stack = [] visitedAry = [] current = 0 maxStore = current maxCount = storeAry[current][1] stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if G1.graph[current][vertex] == 1: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) if storeAry[current][1] > maxCount: maxCount = storeAry[current][1] maxStore = current else: current = stack.pop() print('허니버터칩 최대 보유 편의점(개수) -->', storeAry[maxStore][0], '(', storeAry[maxStore][1], ')')
Practice 3-2 : Undersea Cable MST (Ex09-02)

Find the minimum cost undersea cable connection for 6 cities using Kruskal's algorithm.

class Graph(): def __init__(self, size): self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] def printGraph(g): print(' ', end=' ') for v in range(g.SIZE): print(cityAry[v], end=' ') print() for row in range(g.SIZE): print(cityAry[row], end=' ') for col in range(g.SIZE): print("%2d" % g.graph[row][col], end=' ') print() print() def findVertex(g, findVtx): stack = [] visitedAry = [] current = 0 stack.append(current) visitedAry.append(current) while (len(stack) != 0): next = None for vertex in range(gSize): if g.graph[current][vertex] != 0: if vertex in visitedAry: pass else: next = vertex break if next != None: current = next stack.append(current) visitedAry.append(current) else: current = stack.pop() if findVtx in visitedAry: return True else: return False G1 = None cityAry = ['서울','뉴욕','런던','북경','방콕','파리'] 서울,뉴욕,런던,북경,방콕,파리 = 0,1,2,3,4,5 gSize = 6 G1 = Graph(gSize) G1.graph[서울][뉴욕]=80; G1.graph[서울][북경]=10 G1.graph[뉴욕][서울]=80; G1.graph[뉴욕][북경]=40 G1.graph[뉴욕][방콕]=70 G1.graph[런던][방콕]=30; G1.graph[런던][파리]=60 G1.graph[북경][서울]=10; G1.graph[북경][뉴욕]=40 G1.graph[북경][방콕]=50 G1.graph[방콕][뉴욕]=70; G1.graph[방콕][북경]=50 G1.graph[방콕][런던]=30; G1.graph[방콕][파리]=20 G1.graph[파리][방콕]=20; G1.graph[파리][런던]=60 print('## Undersea Cable Full Graph ##') printGraph(G1) edgeAry = [] for i in range(gSize): for k in range(gSize): if G1.graph[i][k] != 0: edgeAry.append([G1.graph[i][k], i, k]) from operator import itemgetter edgeAry = sorted(edgeAry, key=itemgetter(0), reverse=False) newAry = [] for i in range(0,len(edgeAry),2): newAry.append(edgeAry[i]) index = 0 while (len(newAry) > gSize-1): start = newAry[index][1] end = newAry[index][2] saveCost = newAry[index][0] G1.graph[start][end] = 0 G1.graph[end][start] = 0 startYN = findVertex(G1, start) endYN = findVertex(G1, end) if startYN and endYN: del(newAry[index]) else: G1.graph[start][end] = saveCost G1.graph[end][start] = saveCost index += 1 print('## Most Efficient Cable Connection ##') printGraph(G1)
1 / 22