๐ ๋ฌธ์
๐ญ ์๊ฐ์ ํ๋ฆ
MST (Minimum Spanning Tree) + Undirected Graph
=> 2๊ฐ์ง ๋ฐฉ๋ฒ!
- Kruskcal
- Primโ๏ธ
- mstSet: ๋ฐฉ๋ฌธ๋ MST ์์์ ์งํฉ
- not visited Set: ์์ง ๋ฐฉ๋ฌธํ์ง ์์ ๋ ธ๋ ๋ฒํธ - ๋น์ฉ ์งํฉ
- find minimum cut vertex : mstSet๊ณผ not visited Set์ ์ต์ ๋น์ฉ์ผ๋ก ์ฐ๊ฒฐํ๋ ๋ ธ๋ ์ฐพ๊ธฐ
mstSet -> ์ด์ฐจํผ weight์ ์ดํฉ์ ๊ตฌํ๋ ๊ฒ์ด๋ฏ๋ก ๋ฐฉ๋ฌธ ์์๋ฅผ ์ ์ฅํ ํ์๊ฐ ์๊ฒ ๋ค -> unorderd_set
not visited Set -> ๋ฐฐ์ด๋ก ์์ฑ์ ๋ฐ๋ก visited Boolean ๋ฐฐ์ด์ด ํ์ํ๋ฏ๋ก map ์๋ฃ๊ตฌ์กฐ ์ฌ์ฉ
โญ find minimum cut vertex -> not visted Set ์์ value ์ต์๊ฐ ๊ตฌํ๊ธฐ -> min_element ํจ์ ์ฌ์ฉ
Time Compexity : O(V^2) // N^2/2
Prirority Queue๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ํจ์จ์ ์ด๋ค.
๐ ํ์ด1 (C++)
prim.h
#pragma once
#include <unordered_set>
#include <map>
#include <vector>
#include <algorithm>
class Prim {
private:
enum {
DESTINATION_VERTEX,
WEIGHT
};
struct CompareValue {
bool operator()(const std::pair<int, int>& left, const std::pair<int, int>& right) const {
return left.second < right.second;
}
};
public:
// list representation, with V vertices.
int spanningTree(int V, std::vector<std::vector<int>>& adj);
};
Prim.cpp
#include "Prim.h"
int Prim::spanningTree(int V, std::vector<std::vector<int>>& adj) {
// w: weight (distance) [0,1000]
std::unordered_set<int> mstSet;
std::map<int, int> notVisitedVertices;
for (int i = 0; i < V; i++) {
notVisitedVertices[i] = 1000;
}
// first vertex is 0 for picking first
notVisitedVertices[0] = 0;
int answer = 0;
while (mstSet.size() < V) {
// find minimum cut vertex
std::pair<int, int> minimumVertex = *min_element(notVisitedVertices.begin(), notVisitedVertices.end(), CompareValue());
mstSet.emplace(minimumVertex.first);
answer += minimumVertex.second;
notVisitedVertices.erase(minimumVertex.first);
for (const auto& adjacentVertex : adj[minimumVertex.first]) {
// update key, 0: destination vertex , 1: weight
// not exists in mstSet -> not visited Vertex
if (mstSet.find(adjacentVertex[DESTINATION_VERTEX]) == mstSet.end()) {
// less than previous value -> update
if (adjacentVertex[WEIGHT] < notVisitedVertices[adjacentVertex[DESTINATION_VERTEX]]) {
notVisitedVertices[adjacentVertex[DESTINATION_VERTEX]] = adjacentVertex[WEIGHT];
}
}
// exists -> nothing to do
}
}
return answer;
}
main.cpp
#include "DetectCycleDFS.h"
#include "Prim.h"
#include <iostream>
using namespace std;
int main() {
int V, E;
cin >> V >> E;
vector<vector<int>> adj[V]; // adjacent Matrix
int i = 0;
while (i++ < E) {
int u, v, weight;
cin >> u >> v >> weight;
vector<int> t1, t2;
t1.emplace_back(v);
t1.emplace_back(weight);
adj[u].emplace_back(t1);
t2.emplace_back(u);
t2.emplace_back(weight);
adj[v].emplace_back(t2);
}
Prim obj;
cout << adj->size() << endl;
cout << "result : " << obj.spanningTree(V, adj) << endl;
return 0;
}
๐ ํ์ด2 (Optimization using Priority Queue)
Prim.h
#pragma once
#include <unordered_set>
#include <vector>
#include <queue>
class Prim {
private:
struct Node {
unsigned int nodeNumber;
// max weight is 1000
unsigned int weight = 1000;
};
struct CompareWeight {
bool operator()(const Node& previousNode, const Node& nextNode) const {
return previousNode.weight > nextNode.weight;
}
};
enum {
DESTINATION_VERTEX,
WEIGHT
};
public:
// list representation, with V vertices.
int spanningTree(int V, std::vector<std::vector<int>> adj[]);
};
Prim.cpp
#include "Prim.h"
int Prim::spanningTree(int V, std::vector<std::vector<int>> adj[]) {
// w: weight (distance) [0,1000]
std::unordered_set<unsigned int> mstSet;
struct Node nodes[V];
for (int index = 0; index < V ; index++) {
nodes[index] = {static_cast<unsigned int>(index)};
}
// first vertex is 0 for picking first
nodes[0] = {0, 0};
// auto minHeap Generate and construct prirority queue like this
std::priority_queue<Node, std::vector<Node>, CompareWeight> priorityQueue(nodes, nodes + V);
int answer = 0;
while (mstSet.size() < V) {
// find minimum cut vertex
const Node minimumNode = priorityQueue.top();
priorityQueue.pop();
// there is duplication of nodeNumber
// because C++ STL doesn't support change priority (Decrease key) operation
// emplace || push operation makes duplication
// so check the duplication like this
if (mstSet.find(minimumNode.nodeNumber) != mstSet.end()) continue;
mstSet.emplace(minimumNode.nodeNumber);
answer += minimumNode.weight;
for (const auto& adjacentVertex : adj[minimumNode.nodeNumber]) {
// update key, 0: destination vertex , 1: weight
// not exists in mstSet -> not visited Vertex
if (mstSet.find(adjacentVertex[DESTINATION_VERTEX]) == mstSet.end()) {
// less than previous value -> update
if (adjacentVertex[WEIGHT] < nodes[adjacentVertex[DESTINATION_VERTEX]].weight) {
nodes[adjacentVertex[DESTINATION_VERTEX]].weight = adjacentVertex[WEIGHT];
// need to update update priority queue change priority
priorityQueue.emplace(Node{static_cast<unsigned int>(adjacentVertex[DESTINATION_VERTEX]), static_cast<unsigned int>(adjacentVertex[WEIGHT])});
}
}
// exists -> nothing to do
}
}
return answer;
}
Time Complexity ํ์ด 1 vs ํ์ด2
Prim ์๊ณ ๋ฆฌ์ฆ์ N๊ฐ์ ๋ ธ๋ ๋ชจ๋ MST ์งํฉ์ ํฌํจ๋ ๋๊น์ง ์ฐ์ฐํ๋๋ฐ
๊ฐ์ฅ ์ต์ ๋น์ฉ์ผ๋ก ์ฐ๊ฒฐํ cut vertex ๋ฅผ ์ฐพ๋ ์ฐ์ฐ์์ ์๊ฐ๋ณต์ก๋๊ฐ ์ข์ง์ฐ์ง ๋๋ค.
[ํ์ด1] ๋ ธ๋ ๊ฐฏ์๋งํผ ์กฐ์ฌํด์ผํจ
while (mstSet.size() < V) {
// find minimum cut vertex
std::pair<int, int> minimumVertex = *min_element(notVisitedVertices.begin(), notVisitedVertices.end(), CompareValue());
// ...
}
∴ O(V^2)
[ํ์ด2]
(1) ๋ฐฐ์ด -> ์ต์ ํ ์์ฑ == 2V
(2) ์ฐ์ ์์ ํ getMin() == O(1)
(3) ์ ์์ ์ฝ์ ๋๋ ์ฐ์ ์์ ๋ณ๊ฒฝ == O(E log V)
while (mstSet.size() < V) {
// find minimum cut vertex
const Node minimumNode = priorityQueue.top();
priorityQueue.pop();
if (mstSet.find(minimumNode.nodeNumber) != mstSet.end()) continue;
// ...
}
∴ O (E log V)
โป Tip
์ ์ํ๊ฒ๋ C++ stl ์์ ๊ธฐ๋ณธ ์ ๊ณตํ๋ ์ฐ์ ์์ ํ๋ change priority (== decreaseKey) ๋ฅผ ์ ๊ณตํ์ง ์๋๋ค.
Reference
'Algorithm > Algorithm (๋ฌธ์ ํ์ด)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Geeks For Geeks] Reverse words in a given String (0) | 2021.02.18 |
---|---|
[Geeks For Geeks] Reverse a String (0) | 2021.02.18 |
[GeeksForGeeks] Detect Cycle in a directed graph (0) | 2021.02.05 |
[GeeksForGeeks] Topological Sort using Kahn's Algorithm (0) | 2021.02.04 |
[GeeksForGeeks] Detect cycle using DFS (0) | 2021.02.02 |