IT&컴퓨터공학/자료구조&알고리즘

[알고리즘] 최소신장트리 구하기 - 크루스칼 알고리즘 C++

yan_z 2021. 2. 14. 22:09

앞에서 최소신장트리를 구하는 두가지 알고리즘을 살펴봤다.

두개 중 먼저 크루스칼 알고리즘을 구현해보려 한다.

 

크루스칼 알고리즘

크루스칼 알고리즘은 간선의 cost 를 오름차순으로 정렬하고 제일 cost가 싼 간선 순서대로 연결시키는 알고리즘이다.

이때 사이클이 만들어지면 안되므로, 간선 추가 시 사이클이 발생하는지를 판단하는 알고리즘이 따로 필요한데,

 

이는 Union Find 를 이용하면 된다

 

 

Union Find

총 1 부터 10까지 10개의 노드가 존재한다.

노드는 서로 연결되어 있는데, 

 

1-2-3-4

5-6-7-8

9-10 

 

 총 3 그룹으로 나눌 수 있다.

 

여기서 우리는 Parent[] 라는 배열을 사용하는데, 윗칸은 노드의 번호 , 아래칸은 해당 노드의 부모 노드를 의미한다.

 

연결정보를 갱신하는데, 이때 작은 값을 기준으로 갱신한다.

 

위 그림에서 1 과 2가 서로 연결되어있으므로 

노드'2' 의 부모 노드를 1로 바꾸는 식이다.

 

1-2-3-4 는 모두 한 그룹에 속해있지만 현재는 3과 4의 부모노드 값은 1이 아닌 걸 볼 수 있다.

 

3은 2와 연결되어있다 → 2는 1과 연결되어있다 → 따라서 3과 1은 연결되어있다. 

그러므로 노드'3' 의 부모노드도 1로 바꿔줘야하는데, 이는 재귀를 통해 해결할 수 있다.

 

크루스칼 알고리즘 구현

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int parent[7];

class Edge {
public:
	int node[2];
	int cost;
	Edge(int a, int b, int cost) {
		this->node[0] = a;
		this->node[1] = b;
		this->cost = cost;
	}

	//간선을 오름차순으로 정렬할때 기준을 cost로 정함.
	bool operator<(Edge &edge) {
		return this->cost < edge.cost;
	}

};

//재귀로 구현한 부모노드 가져오는 함수
int getParent(int node) {
	if (parent[node] == node) return node;
	else return getParent(parent[node]);
}

// 노드끼리 연결시키는 함수. 이때 부모노드는 작은 노드로 쓴다.
void unionParent(int node1, int node2) {
	node1 = getParent(node1);
	node2 = getParent(node2);
	if (node1 < node2) parent[node2] = node1;
	else parent[node1] = node2;
}

// 사이클인지 판단하는 함수
bool isCycle(int node1, int node2) {
	node1 = getParent(node1);
	node2 = getParent(node2);
	if (node1 == node2) return true;
	else return false;
}

int main() {
	//두 노드를 연결할 간선을 정해줍니다. 
	vector<Edge> v;
	v.push_back(Edge(1, 7, 12));
	v.push_back(Edge(1, 4, 23));
	v.push_back(Edge(1, 2, 26));
	v.push_back(Edge(2, 3, 36));
	v.push_back(Edge(2, 4, 21));
	v.push_back(Edge(2, 5, 45));
	v.push_back(Edge(3, 5, 29));
	v.push_back(Edge(3, 6, 37));
	v.push_back(Edge(3, 7, 55));
	v.push_back(Edge(4, 7, 20));
	v.push_back(Edge(5, 6, 30));

	//간선을 오름차순으로 정렬합니다. 
	sort(v.begin(), v.end());

	//각 노드는 자기자신이 부모로 초기화해줍니다. 
	for (int i = 1; i <= 7; ++i) {
		parent[i] = i;
	}

	int sum = 0;
	for (int i = 0; i < v.size(); i++) {
		//싸이클이 존재하지 않으면 비용을 더합니다. 
		if (isCycle(v[i].node[0], v[i].node[1])==false) {
			sum += v[i].cost;
			unionParent(v[i].node[0], v[i].node[1]);
		}
	}

	cout << sum << endl;

	return 0;
}