๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
์•Œ๊ณ ๋ฆฌ์ฆ˜/์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ข…๋ฅ˜

[MST] ์ตœ์†Œ ์ŠคํŒจ๋‹ ํŠธ๋ฆฌ - ํฌ๋ฃจ์Šค์นผ(Kruskal), ํ”„๋ฆผ(Prim)

by ๐Ÿณ Laboon 2024. 7. 11.
MST๋ž€?

 

์ตœ์†Œ ์‹ ์žฅ ํŠธ๋ฆฌ(Minimum Spanning Tree)๋กœ ๊ฐ€์ค‘ ๊ทธ๋ž˜ํ”„(Weight Graph)์—์„œ ๊ฐ€์žฅ ์ ์€ ๋น„์šฉ(Weight)์œผ๋กœ ๋ชจ๋“  ์ •์ (Vertex)์œผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„ ๊ทธ๋ž˜ํ”„(Sub Graph)์ด๋‹ค. MST๋Š” ๊ฐ ์ •์ ์—์„œ ์ •์  ์‚ฌ์ด์— ํ•˜๋‚˜์˜ ๊ฐ„์„ (Edge)๋งŒ ํ•„์š”ํ•˜๋ฏ€๋กœ ์ด N-1 ๊ฐœ์˜ ๊ฐ„์„ ์„ ๊ฐ€์ง€๊ฒŒ ๋˜๋ฏ€๋กœ ๋งˆ์น˜ ํŠธ๋ฆฌ์˜ ํ˜•ํƒœ๋ฅผ ํ•˜๊ณ  ์žˆ์–ด์„œ ํŠธ๋ฆฌ๋ผ๋Š” ์ด๋ฆ„์ด ๋ถ™๋Š”๋‹ค.


ํฌ๋ฃจ์Šค์นผ ์•Œ๊ณ ๋ฆฌ์ฆ˜

 

1. ๋ชจ๋“  ๊ฐ„์„ ์„ ๋น„์šฉ์ด ์ ์€ ์ˆœ์„œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•œ๋‹ค. - Sort

2. ๊ฐ„์„ ์— ๋ถ™์€ ์ •์  u์™€ v๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ถ€๋ชจ๋ฅผ ์ฐพ๋Š”๋‹ค. - Find

3. ์ •์  u์˜ ๋ถ€๋ชจ์™€ ์ •์  v์˜ ๋ถ€๋ชจ๊ฐ€ ๋‹ค๋ฅด๋‹ค๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ ์ง‘ํ•ฉ์— ์†ํ•ด์žˆ์ง€๋งŒ, 1๋ฒˆ ๊ณผ์ •์œผ๋กœ ์ธํ•ด ํ•ด๋‹น ๊ฐ„์„ ์ด ๋‘ ์ •์ ์˜ ์ตœ์†Œ ๋น„์šฉ์˜ ๊ฐ„์„ ์ž„์„ ์•Œ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ•˜๋‚˜์˜ ์ง‘ํ•ฉ์œผ๋กœ ํ•ฉ์นœ๋‹ค. - Union

 

+ Union ๊ณผ์ •์—์„œ ์ด์–ด์ง„ ๊ฐ„์„ ์ด ์ตœ์†Œ ๋น„์šฉ์ด๋ฏ€๋กœ ํ•ด๋‹น ๊ฐ„์„  ์ •๋ณด๋กœ MST์˜ ์ด ๋น„์šฉ์ด๋‚˜ ๊ฒฝ๋กœ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‹ค!

+ 1๋ฒˆ Sortํ•˜๋Š” ๊ณผ์ •์„ ๋ฐฐ์—ด๋กœ ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, Priority Queue๋กœ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํฌ๋ฃจ์Šค์นผ ๋…ธ๋“œ
package com.study.datastructrue.graph.mst.kruskal;

public class KruskalNode implements Comparable<KruskalNode> {

    int u;
    int v;
    int weight;

    public KruskalNode(int u, int v, int weight) {
        this.u = u;
        this.v = v;
        this.weight = weight;
    }

    @Override
    public int compareTo(KruskalNode o) {
        return this.weight - o.weight;
    }

}

์ •๋ ฌ์„ ์œ„ํ•ด Comparable ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์ค€๋‹ค.

 

Kruskal Algorithm - Array
package com.study.datastructrue.graph.mst.kruskal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public class ArrayKruskal {

    private List<KruskalNode> edges;
    private int[] parent, ranks;
    private int minimumDistance;
    private Set<Integer> path;

    public ArrayKruskal(int size) {
        this.edges = new ArrayList<>();
        this.parent = new int[size + 1];
        this.ranks = new int[size + 1];
        this.minimumDistance = Integer.MAX_VALUE;
        this.path = new LinkedHashSet<>();
        initParent(size + 1);
    }

    public void update() {
        this.minimumDistance = 0;
        Collections.sort(this.edges);
        for (KruskalNode node : this.edges) {
            int parentA = find(node.u);
            int parentB = find(node.v);
            if (parentA != parentB) {
                union(parentA, parentB);
                this.minimumDistance += node.weight;
                this.path.add(node.u);
                this.path.add(node.v);
            }
        }
    }

    public int getMinimumDistance() {
        return minimumDistance;
    }

    public void printTrace() {
        System.out.println(path.toString());
    }

    public void add(int u, int v, int w) {
        this.edges.add(new KruskalNode(u, v, w));
    }

    private void initParent(int size) {
        for (int i = 0; i < size; i++) {
            this.parent[i] = i;
            this.ranks[i] = 0; // rank ์ดˆ๊ธฐํ™”
        }
    }

    private int find(int x) {
        if (x != parent[x]) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    private void union(int x, int y) {
        if (ranks[x] < ranks[y]) {
            int temp = ranks[x];
            ranks[x] = ranks[y];
            ranks[y] = temp;
        }

        parent[y] = x;

        if(ranks[x] == ranks[y]) {
            ranks[x] = ranks[y] + 1;
        }
    }

}

1. find ์—ฐ์‚ฐ์€ ๋ถ€๋ชจ๋ฅผ ์ฐพ์„ ๋•Œ๊นŒ์ง€ recursion์„ ์ง„ํ–‰ํ•œ๋‹ค. (์„ฑ๋Šฅ ๊ฐœ์„ , ๊ฒฝ๋กœ์••์ถ• ๊ธฐ๋ฒ• ์ ์šฉ)

2. ์ •์  u์™€ ์ •์  v๊ฐ€ ๋‹ค๋ฅผ ๊ฒฝ์šฐ Union ์—ฐ์‚ฐ์ด ์ง„ํ–‰๋œ๋‹ค. ์ด ๋•Œ, Rank๋กœ ํŠธ๋ฆฌ์˜ ๋†’์ด๋ฅผ ์ €์žฅํ•˜๋ฉด์„œ ๊ฐ tree์˜ ๊ณ„์ธต์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. (Union ์—ฐ์‚ฐ์˜ ๊ฐœ์„ )

3. Set ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•ด Union ์—ฐ์‚ฐ๋งˆ๋‹ค ๋งค ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค‘๋ณต๋œ ๋…ธ๋“œ๋Š” ๋ฌด์‹œํ•˜๊ณ  ๊ฒฝ๋กœ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Kruskal Algorithm - Priority Queue
package com.study.datastructrue.graph.mst.kruskal;

import java.util.LinkedHashSet;
import java.util.PriorityQueue;
import java.util.Set;

public class PriorityQueueKruskal {

    private PriorityQueue<KruskalNode> edges;
    private Set<Integer> path;
    private int[] parent, ranks;
    private int minimumDistance;

    public PriorityQueueKruskal(int size) {
        this.edges = new PriorityQueue<>();
        this.parent = new int[size + 1];
        this.ranks = new int[size + 1];
        this.minimumDistance = Integer.MAX_VALUE;
        this.path = new LinkedHashSet<>();
        initParent(size + 1);
    }

    public void update() {
        this.minimumDistance = 0;
        PriorityQueue<KruskalNode> temp = new PriorityQueue<>(this.edges);
        while (!temp.isEmpty()) {
            KruskalNode node = temp.poll();
            int parentA = find(node.u);
            int parentB = find(node.v);
            if (parentA != parentB) {
                union(parentA, parentB);
                this.minimumDistance += node.weight;
                this.path.add(node.u);
                this.path.add(node.v);
            }
        }
    }

    public int getMinimumDistance() {
        return minimumDistance;
    }

    public void printTrace() {
        System.out.println(path.toString());
    }

    public void add(int u, int v, int w) {
        this.edges.add(new KruskalNode(u, v, w));
    }

    private void initParent(int size) {
        for (int i = 0; i < size; i++) {
            this.parent[i] = i;
        }
    }

    private int find(int x) {
        if (x == parent[x]) {
            return x;
        }
        return parent[x] = find(parent[x]);
    }

    private void union(int x, int y) {
        if (x == y) {
            return;
        }

        if (ranks[x] < ranks[y]) {
            int temp = ranks[x];
            ranks[x] = ranks[y];
            ranks[y] = temp;
        }

        parent[y] = x;

        if(ranks[x] == ranks[y]) {
            ranks[x] = ranks[y] + 1;
        }
    }

}

- Priority Queue๋Š” poll ์—ฐ์‚ฐ์„ ํ†ตํ•ด ์ตœ์†Œ ์‹ ์žฅ ํŠธ๋ฆฌ๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ผํšŒ์šฉ์ด๋ฏ€๋กœ ๋ณต์‚ฌํ•˜๊ฒŒ ๋˜๋Š” ์‹œ๊ฐ„์ด ๋ฐœ์ƒํ•œ๋‹ค. 

- ๋”ฐ๋กœ ์ •๋ ฌ ๊ฐ์ฒด๋ฅผ ๋ถ€๋ฅด์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ ์Œ.. Priority Queue๊ฐ€ ํŽธํ•˜๋ฉด ์ด ๋ฐฉ๋ฒ•์„, ์•„๋‹ˆ๋ฉด ArrayList ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๊ณ  ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ๋‹ค ์ž‘์„ฑํ–ˆ๋‹ค.

 

์‹ค์‚ฌ์šฉ Main
package com.study.datastructrue.graph.mst.kruskal;

public class Main {

    public static void main(String[] args) {
        PriorityQueueKruskal pKruskal = new PriorityQueueKruskal(5);
        pKruskal.add(1, 2, 6);      pKruskal.add(1, 3, 3);
        pKruskal.add(1, 4, 1);      pKruskal.add(2, 5, 4);
        pKruskal.add(3, 4, 2);      pKruskal.add(3, 5, 5);
        pKruskal.add(4, 5, 7);      pKruskal.update();

        System.out.println(pKruskal.getMinimumDistance());
        pKruskal.printTrace();

        ArrayKruskal aKruskal = new ArrayKruskal(5);
        aKruskal.add(1, 2, 6);      aKruskal.add(1, 3, 3);
        aKruskal.add(1, 4, 1);      aKruskal.add(2, 5, 4);
        aKruskal.add(3, 4, 2);      aKruskal.add(3, 5, 5);
        aKruskal.add(4, 5, 7);      aKruskal.update();

        System.out.println(aKruskal.getMinimumDistance());
        aKruskal.printTrace();
    }

}

ํ”„๋ฆผ ์•Œ๊ณ ๋ฆฌ์ฆ˜

 

1. ๋ชจ๋“  ๊ฐ„์„ ์ด ๋น„์šฉ์ด ์ ์€ ์ˆœ์„œ๋กœ ์ •๋ ฌํ•œ๋‹ค. - Sort

2. ์‹œ์ž‘ ์ •์ ์„ ์ •ํ•œ๋‹ค.

3. ์‹œ์ž‘ ์ •์ ์œผ๋กœ๋ถ€ํ„ฐ ๋ฐฉ๋ฌธํ•˜์ง€ ์•Š์€ ์ •์ ๊ณผ ์—ฐ๊ฒฐ๋œ ๊ฐ„์„ ๋งŒ ์ตœ์†Œ ๋น„์šฉ ๊ฐ„์„ ์œผ๋กœ ์ฑ„ํƒํ•œ๋‹ค.

4. 3๋ฒˆ์—์„œ ๊ตฌํ•œ ์ •์ ๊ณผ์˜ ์ตœ์†Œ ๋น„์šฉ ๊ฐ„์„ ์„ ๊ตฌํ•˜๋Š” ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•œ๋‹ค.

 

+  3๋ฒˆ, 4๋ฒˆ ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ณผ์ •์„ ํ•˜๋ ค๋ฉด PriorityQueue๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

+ ์ตœ์†Œ ๋น„์šฉ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐฉ๋ฌธํ•˜์ง€ ์•Š์€ ์ •์ ๋งŒ ๋ฐฉ๋ฌธํ•˜๋ฏ€๋กœ ์ตœ์†Œ ๋น„์šฉ ๊ฒฝ๋กœ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํ”„๋ฆผ ๋…ธ๋“œ
package com.study.datastructrue.graph.mst.prim;

public class PrimNode implements Comparable<PrimNode>{

    int v;
    int weight;

    public PrimNode(int v, int weight) {
        this.v = v;
        this.weight = weight;
    }

    @Override
    public int compareTo(PrimNode o) {
        return this.weight - o.weight;
    }

}

- ํฌ๋ฃจ์Šค์นผ์€ ๊ฐ„์„ ์˜ ๋น„์šฉ์„ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— u, v ์ •๋ณด๊ฐ€ ๋ชจ๋‘ ํ•„์š”ํ•˜๋‹ค.

- ํ”„๋ฆผ์€ ๋‹ค์Œ ์ •์ ๊นŒ์ง€์˜ ๋น„์šฉ์„ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— u ์ •๋ณด๊ฐ€ ํ•„์š” ์—†๋‹ค.

 

Prim Algorithm
package com.study.datastructrue.graph.mst.prim;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;

public class Prim {

    private List<PrimNode>[] graph;
    private List<Integer> path;
    private boolean[] visited;
    private int minimumDistance;

    public Prim(int size) {
        this.graph = new List[size + 1];
        this.path = new ArrayList<>();
        this.visited = new boolean[size + 1];
        this.minimumDistance = Integer.MAX_VALUE;
        initGraph(size);
    }

    private void initGraph(int size) {
        for (int i = 0; i <= size; i++) {
            this.graph[i] = new ArrayList<>();
        }
    }

    public void add(int u, int v, int w) {
        this.graph[u].add(new PrimNode(v, w));
        this.graph[v].add(new PrimNode(u, w));
    }

    public void update(int start) {
        Arrays.fill(visited, false);
        path.clear();
        this.minimumDistance = 0;

        PriorityQueue<PrimNode> pq = new PriorityQueue<>();
        pq.offer(new PrimNode(start, 0));

        while(!pq.isEmpty()) {
            PrimNode node = pq.poll();

            if (visited[node.v]) continue;
            visited[node.v] = true;
            this.minimumDistance += node.weight;
            path.add(node.v);

            for (PrimNode next: graph[node.v]) {
                if (!visited[next.v]) {
                    pq.offer(next);
                }
            }
        }
    }

    public int getMinimumDistance() {
        return this.minimumDistance;
    }

    public void printTrace() {
        for (int v: path) {
            System.out.print(v + " ");
        }
        System.out.println();
    }

}

- Path๋Š” ๋ฐฉ๋ฌธํ•˜์ง€ ์•Š์€ ์ •์  ์ˆœ์„œ๋Œ€๋กœ ์ตœ์†Œ ๋น„์šฉ์œผ๋กœ ์ฑ„ํƒ๋ ํ…Œ๋‹ˆ List ๋ฅผ ํ†ตํ•ด ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

 

Main
package com.study.datastructrue.graph.mst.prim;

import com.study.datastructrue.graph.mst.kruskal.PriorityQueueKruskal;

public class Main {

    public static void main(String[] args) {
        Prim prim = new Prim(5);
        prim.add(1, 2, 6);      prim.add(1, 3, 3);
        prim.add(1, 4, 1);      prim.add(2, 5, 4);
        prim.add(3, 4, 2);      prim.add(3, 5, 5);
        prim.add(4, 5, 7);      prim.update(1);

        System.out.println(prim.getMinimumDistance());
        prim.printTrace();
    }

}

 

๋ณดํ†ต ๊ฐ„์„ ์ด ๋งŽ์€ ๊ฒฝ์šฐ, ๊ฐ„์„ ์„ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ํฌ๋ฃจ์Šค์นผ

์ •์ ์ด ๋งŽ์€ ๊ฒฝ์šฐ, ์ •์ ์„ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ํ”„๋ฆผ์„ ์‚ฌ์šฉํ•˜๋ฉด ํšจ์œจ์ ์ด๊ฒ ๋‹ค.