Skip to content

그래프 · 최단 경로 · 최소 신장 트리

그래프의 기본 용어와 표현 방법을 정리하고, 대표적인 두 문제인 최단 경로(다익스트라)와 최소 신장 트리(프림·크루스칼)를 살펴본다.

그래프

그래프graph G=(V,E)는 정점vertex V와 간선edge E의 집합이다.

  • 각 간선은 순서쌍 (v,w)로 표현된다(단 v,wV).
  • 간선을 나타내는 순서쌍에 순서가 있으면 그 그래프는 유향directed이다.
  • (v,w)E일 때에만 정점 w가 정점 v에 인접adjacent했다고 한다.
  • 무향 그래프undirected graph에서 간선 (v,w)가 있으면 (w,v)도 성립하며, 두 정점은 서로 인접한다.
  • 간선은 때때로 세 번째 요소component인 가중치weight 또는 비용cost을 가진다.

경로와 순환 관련 용어를 정리하면 다음과 같다.

  • 경로path: 정점들의 나열 w1,w2,,wN으로, (wi,wi+1)E(1i<N)를 만족한다.
  • 경로의 길이length: 경로 위 간선의 수 N1. 간선이 없는 자기 자신으로의 경로는 길이 0이다.
  • 루프loop: 간선 (v,v), 즉 자기 자신으로 향하는 경로.
  • 단순 경로simple path: 처음과 마지막 정점을 제외하고 방문하는 모든 정점이 서로 다른 경로.
  • 순환cycle: 유향 그래프에서 길이가 최소 1이고 w1=wN인 경로. 순환이 없으면 비순환acyclic이다.
    • 무향 그래프에서 u,v,u는 순환으로 보지 않는다. (u,v)(v,u)가 같은 간선이기 때문이다.
    • 유향 그래프에서는 (u,v)(v,u)가 다른 간선이므로 u,v,u를 순환이라 부를 수 있다.

연결성도 그래프의 중요한 성질이다.

  • 무향 그래프에서 모든 정점이 서로 연결되어 있으면 연결connected되었다고 한다.
  • 유향 그래프가 (방향을 고려해도) 연결되어 있으면 강하게 연결strongly connected되었다고 한다.
    • 방향을 모두 없앤 기저 그래프만 연결되어 있으면 약하게 연결weakly connected되었다고 한다.
  • 모든 정점 쌍 사이에 간선이 있으면 완전 그래프complete graph라고 한다.
유향 그래프 예시
루프를 포함한 유향 그래프 예시

인접 행렬 vs 인접 리스트

그래프를 표현하는 두 가지 대표적 방법이 있다.

  • 인접 행렬adjacency matrix: 2차원 배열로, 간선 (u,v)가 있으면 A[u][v]true, 없으면 false로 둔다.
    • 가중치가 있으면 A[u][v]를 가중치 값으로 두고, 없는 간선은 아주 크거나 아주 작은 값으로 미리 채운다.
    • 공간이 Θ(|V|2)이므로 간선이 적은 그래프에는 낭비가 크다. 밀집dense(|E|=Θ(|V|2)) 그래프에 적합하다.
  • 인접 리스트adjacency list: 각 정점마다 인접한 정점들을 나열한다.
    • 공간이 O(|E|+|V|)로 그래프 크기에 선형 비례하므로, 희소sparse한 그래프에 적절하다.
A=[0110001110000111001000010]
인접 리스트 표현
같은 그래프의 인접 리스트 표현

최소 경로 문제

가중치 그래프에서 간선 (vi,vj)를 지나는 데 비용 ci,j가 든다고 할 때, 최소 경로shortest-path 문제를 생각할 수 있다.

  • 경로 v1v2vN의 비용은 i=1N1ci,i+1이며, 이를 가중치 경로 길이weighted path length라고 한다.
  • 비가중치 경로 길이unweighted path length는 경로 위 간선의 개수 N1이다.
  • 비가중치 문제는 모든 간선의 가중치가 1 가중치 최소 경로 문제의 특수한 경우이다.

아래 그래프에서 v1에서 v6으로 가는 최소 비용 경로는 v1v4v7v6으로 비용 6이며, 비가중치로 보면 최단 길이는 2이다.

가중치 유향 그래프
최소 경로 문제의 예시 가중치 유향 그래프

다익스트라 알고리즘

단일-소스 최소 경로 문제single-source shortest-path problem를 푸는 대표적인 방법이 다익스트라 알고리즘Dijkstra's algorithm이다. 비가중치 경우와 비슷한 아이디어를 쓰되, 각 정점에 다음 정보를 유지한다.

  1. 각 정점을 known 또는 unknown으로 표기한다.
  2. 시험적인 거리 dv를 각 정점에 기록한다.
  3. 이 거리는 해당 정점이 known이 될 때 비로소 최소 경로로 확정된다.
  4. pv에는 dv를 마지막으로 바꾸게 만든 정점을 기록한다.

각 단계에서 아직 unknown인 정점 중 dv가 가장 작은 정점 v를 선택known으로 선언하고, v에 인접한 정점들의 d 값을 갱신한다. 인접 노드 중 최솟값을 찾는 데 한 단계마다 O(|V|)가 들어, 배열 기반 구현의 전체 복잡도는 O(|V|2)이다.

위 그래프에서 v1을 시작점으로 알고리즘을 끝까지 수행한 최종 결과는 다음과 같다.

정점knowndvpv
v1T00
v2T2v1
v3T3v4
v4T1v1
v5T3v4
v6T6v7
v7T5v4

음의 간선 주의

그래프에 음의 간선 비용이 있으면 다익스트라 알고리즘은 정상 동작하지 않는다. 보통은 모든 간선에 상수 Δ를 더해 음의 간선을 없앤 뒤 알고리즘을 적용한다.

실습: 다익스트라 알고리즘

위 그래프를 인접 리스트로 구성하고, 우선순위 큐를 이용해 v1에서 각 정점까지의 최단 거리를 구한다. 출력이 위 최종 표의 dv 열과 정확히 일치하는지 확인해 보자.

cpp
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;

int main() {
    const int N = 7;                 // 정점 v1..v7 (인덱스 1..7)
    vector<vector<pair<int,int>>> adj(N + 1);   // adj[u] = {(v, cost), ...}
    auto add = [&](int u, int v, int w){ adj[u].push_back({v, w}); };

    add(1,2,2); add(1,4,1);
    add(2,4,3); add(2,5,10);
    add(3,1,4); add(3,6,5);
    add(4,3,2); add(4,5,2); add(4,6,8); add(4,7,4);
    add(5,7,6);
    add(7,6,1);

    int s = 1;
    vector<int> dist(N + 1, INT_MAX);
    dist[s] = 0;
    // (거리, 정점) 최소 힙
    priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
    pq.push({0, s});

    while (!pq.empty()) {
        auto [d, u] = pq.top(); pq.pop();
        if (d > dist[u]) continue;       // 이미 더 짧은 경로로 확정된 정점
        for (auto [v, w] : adj[u]) {
            if (dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                pq.push({dist[v], v});
            }
        }
    }

    cout << "v1 에서 각 정점까지의 최단 거리:" << endl;
    for (int v = 1; v <= N; ++v)
        cout << "  v" << v << " : " << dist[v] << endl;
    return 0;
}

무가중 · 위상 정렬

모든 간선의 가중치가 같은 무가중 그래프에서는 단순한 BFS로 최단 거리를 구할 수 있다. 또한 방향성 비순환 그래프(DAG)라면 위상 정렬 순서대로 정점을 처리해 음의 가중치가 있어도 한 번의 순회로 최단 경로를 계산할 수 있다.

최소 신장 트리

그래프 G의 최소 신장 트리minimum spanning treeG의 모든 정점을 가장 적은 비용으로 연결하는 트리이다. G가 연결되어 있을 때에만 정의된다.

원본 가중치 그래프
원본 무향 가중치 그래프
최소 신장 트리
위 그래프의 최소 신장 트리

프림 알고리즘

프림 알고리즘Prim's algorithm은 트리를 한 정점씩 키워 가는 방식으로, 최소 경로 문제의 다익스트라 알고리즘과 근본적으로 동일하다.

  • 각 정점에 대해 dv, pv, known/unknown 여부를 기록한다.
  • dvvknown 정점 집합에 연결하는 최소 간선의 비용, pvdv를 바꾼 마지막 정점이다.
  • 정점 v가 선택되면, 인접한 각 unknown 정점 w에 대해 dw=min(dw,cw,v)로 갱신한다.

다익스트라와 마찬가지로 배열 기반 구현은 O(|V|2)이다. 아래는 v1에서 시작한 프림 알고리즘이 트리를 키워 가는 과정이다.

프림 1단계
프림 1단계
프림 2단계
프림 2단계
프림 3단계
프림 3단계
프림 4단계
프림 4단계
프림 5단계
프림 5단계
프림 6단계
프림 6단계 — 완성된 최소 신장 트리

크루스칼 알고리즘

크루스칼 알고리즘Kruskal's algorithm은 여러 트리가 모인 포레스트forest에서 출발한다.

  • 비용이 작은 간선부터 순서대로 선택하되, 순환을 만들지 않는 간선만 추가한다.
  • 알고리즘이 끝나면 트리 하나만 남고, 그것이 최소 신장 트리이다.
  • 수행 시간은 O(|E|log|E|)이다. 순환 여부 판정 비용은 이보다 작다.
크루스칼 1단계
크루스칼 1단계
크루스칼 2단계
크루스칼 2단계
크루스칼 3단계
크루스칼 3단계
크루스칼 4단계
크루스칼 4단계 (두 개의 부분 트리)
크루스칼 5단계
크루스칼 5단계
크루스칼 6단계
크루스칼 6단계 — 완성된 최소 신장 트리

성능 비교

알고리즘방식시간 복잡도(배열 기반)
프림트리를 한 정점씩 키움(정점 중심)O(|V|2)
크루스칼작은 간선부터 선택, 순환 회피(간선 중심)O(|E|log|E|)

프림은 정점이 적고 간선이 많은 밀집 그래프에, 크루스칼은 간선이 적은 희소 그래프에 유리한 경향이 있다. 크루스칼은 순환 여부 판정을 위해 다음 페이지의 서로소 집합 자료구조를 활용한다.

PDF