无向图,保证所有点都连通的情况下,权重最小。
使用到 并查集 :
//Kruskal算法
class UnionFind {
private:
//key:某个节点, value: key节点往上的点
unordered_map<Vertex*, Vertex*> father;
//key节点所在集合的节点数
unordered_map<Vertex*, int> size;
public:
UnionFind() {}
void init(vector<Vertex*> &vs) {
father.clear();
size.clear();
for (Vertex *v : vs) {
father[v] = v;
size[v] = 1;
}
}
Vertex* find(Vertex *v) {
return father[v] == v ? v : father[v] = find(father[v]);
}
void merge(Vertex *v1, Vertex *v2) {
Vertex *f1 = find(v1), *f2 = find(v2);
if (f1 != f2) {
if (size[f1] >= size[f2]) {
father[f2] = f1;
size[f1] += size[f2];
size.erase(f2);
} else {
father[f1] = f2;
size[f2] += size[f1];
size.erase(f1);
}
}
}
};
struct EdgeComparator {
bool operator()(Edge* const &e1, Edge* const &e2) const {
return e1->weight > e2->weight;
}
};
unordered_set<Edge*> kruskalMST(Graph *graph) {
UnionFind *uf = new UnionFind();
vector<Vertex*> vs;
for (auto &kv : graph->vertexs) {
vs.push_back(kv.second);
}
uf->init(vs);
//建立边的小根堆
priority_queue<Edge*, vector<Edge*>, EdgeComparator> que;
for (auto &e : graph->edges) { //M条边
que.push(e);//O(logM)
}
unordered_set<Edge*> result;
while (!que.empty()) { //M条边
Edge *edge = que.top(); //O(logM)
que.pop();
if (uf->find(edge->from) != uf->find(edge->to)) { //O(1)
result.insert(edge);
uf->merge(edge->from, edge->to);
cout << "选择的边:[" << edge->weight << ", " << edge->from->value << ", " << edge->to->value << "]" << endl;
}
}
return result;
}
int main() {
GraphGenerator *gg = new GraphGenerator();
cout << "Kruskal:" << endl;
vector<vector<int> > infos1 {
{1, 1, 2},
{3, 1, 4},
{5, 1, 3},
{2, 4, 3},
{3, 4, 2},
{5, 2, 3},
};
//createGraph函数的实现见https://blog.csdn.net/u011386173/article/details/126275411?spm=1001.2014.3001.5501
Graph *g1 = gg->createGraph(infos1);
unordered_set<Edge*> res = kruskalMST(g1);
for (Edge *e : res) {
cout << e->weight << endl;
}
return 0;
}
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.Stack;
//undirected graph only
public class Kruskal {
// Union-Find Set
public static class UnionFind {
// key: 某一个节点, value: key节点往上的节点
private HashMap<Node, Node> fatherMap;
// key: 某一个集合的代表节点, value: key所在集合的节点个数
private HashMap<Node, Integer> sizeMap;
public UnionFind() {
fatherMap = new HashMap<Node, Node>();
sizeMap = new HashMap<Node, Integer>();
}
public void makeSets(Collection<Node> nodes) {
fatherMap.clear();
sizeMap.clear();
for (Node node : nodes) {
fatherMap.put(node, node);
sizeMap.put(node, 1);
}
}
private Node findFather(Node n) {
Stack<Node> path = new Stack<>();
while(n != fatherMap.get(n)) {
path.add(n);
n = fatherMap.get(n);
}
while(!path.isEmpty()) {
fatherMap.put(path.pop(), n);
}
return n;
}
public boolean isSameSet(Node a, Node b) {
return findFather(a) == findFather(b);
}
public void union(Node a, Node b) {
if (a == null || b == null) {
return;
}
Node aDai = findFather(a);
Node bDai = findFather(b);
if (aDai != bDai) {
int aSetSize = sizeMap.get(aDai);
int bSetSize = sizeMap.get(bDai);
if (aSetSize <= bSetSize) {
fatherMap.put(aDai, bDai);
sizeMap.put(bDai, aSetSize + bSetSize);
sizeMap.remove(aDai);
} else {
fatherMap.put(bDai, aDai);
sizeMap.put(aDai, aSetSize + bSetSize);
sizeMap.remove(bDai);
}
}
}
}
public static class EdgeComparator implements Comparator<Edge> {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
}
public static Set<Edge> kruskalMST(Graph graph) {
UnionFind unionFind = new UnionFind();
unionFind.makeSets(graph.nodes.values());
// 从小的边到大的边,依次弹出,小根堆!
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
for (Edge edge : graph.edges) { // M 条边
priorityQueue.add(edge); // O(logM)
}
Set<Edge> result = new HashSet<>();
while (!priorityQueue.isEmpty()) { // M 条边
Edge edge = priorityQueue.poll(); // O(logM)
if (!unionFind.isSameSet(edge.from, edge.to)) { // O(1)
result.add(edge);
unionFind.union(edge.from, edge.to);
}
}
return result;
}
}
可以从任意节点出发来寻找最小生成树
某个点加入到被选取的点中后,解锁这个点出发的所有新的边
在所有解锁的边中选最小的边,然后看看这个边会不会形成环
如果会,不要当前边,继续考察剩下解锁的边中最小的边,重复3)
如果不会,要当前边,将该边的指向点加入到被选取的点中,重复2)
当所有点都被选取,最小生成树就得到了
struct EdgeComparator {
bool operator()(Edge* const &e1, Edge* const &e2) const {
return e1->weight > e2->weight;
}
};
//Prim 算法
unordered_set<Edge*> primMST(Graph *graph) {
//解锁的边进入小根堆
priority_queue<Edge*, vector<Edge*>, EdgeComparator> que;
//解锁的点
unordered_set<Vertex*> vertexSet;
//存放选择的边
unordered_set<Edge*> result;
for (auto &kv : graph->vertexs) { //随便选择一个点
Vertex *v = kv.second; //v为开始点
if (!vertexSet.count(v)) {
vertexSet.insert(v);
for (Edge *edge : v->adjE) { //由一个点解锁相连的边,注意此处的v是fromV,所以如果是无向图同一条边需要输入两次
que.push(edge);
}
while (!que.empty()) {
Edge *edge = que.top(); //获取解锁的边中权重最小的边
que.pop();
Vertex *toV = edge->to; //可能的新点
if (!vertexSet.count(toV)) { //集合中不包含时就是新的点
vertexSet.insert(toV);
cout << "选择的边:[" << edge->weight << ", " << edge->from->value << ", " << edge->to->value << "]" << endl;
result.insert(edge);
for (Edge *nextEdge : toV->adjE) {
que.push(nextEdge);
}
}
}
}
//break; //如果就是一张图,那就需要break(只需要一个起始点); 如果是森林,那就不用break(多个起始点)
}
return result;
}
// 请保证graph是连通图
// graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路
// 返回值是最小连通图的路径之和
int prim(vector<vector<int>> &graph) {
int size = graph.size();
vector<int> distances(size);
vector<bool> visit(size);
visit[0] = true;
for (int i = 0; i < size; i++) {
distances[i] = graph[0][i];
}
int sum = 0;
for (int i = 1; i < size; i++) {
int minPath = INT_MAX;
int minIndex = -1;
for (int j = 0; j < size; j++) {
if (!visit[j] && distances[j] < minPath) {
minPath = distances[j];
minIndex = j;
}
}
if (minIndex == -1) {
return sum;
}
visit[minIndex] = true;
sum += minPath;
for (int j = 0; j < size; j++) {
if (!visit[j] && distances[j] > graph[minIndex][j]) {
distances[j] = graph[minIndex][j];
}
}
}
return sum;
}
import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
// undirected graph only
public class Prim {
public static class EdgeComparator implements Comparator<Edge> {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
}
public static Set<Edge> primMST(Graph graph) {
// 解锁的边进入小根堆
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
// 哪些点被解锁出来了
HashSet<Node> nodeSet = new HashSet<>();
Set<Edge> result = new HashSet<>(); // 依次挑选的的边在result里
for (Node node : graph.nodes.values()) { // 随便挑了一个点
// node 是开始点
if (!nodeSet.contains(node)) {
nodeSet.add(node);
for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边
priorityQueue.add(edge);
}
while (!priorityQueue.isEmpty()) {
Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边
Node toNode = edge.to; // 可能的一个新的点
if (!nodeSet.contains(toNode)) { // 不含有的时候,就是新的点
nodeSet.add(toNode);
result.add(edge);
for (Edge nextEdge : toNode.edges) {
priorityQueue.add(nextEdge);
}
}
}
}
// break; //如果就是一张图,那就需要break;如果是森林,就不用break
}
return result;
}
// 请保证graph是连通图
// graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路
// 返回值是最小连通图的路径之和
public static int prim(int[][] graph) {
int size = graph.length;
int[] distances = new int[size];
boolean[] visit = new boolean[size];
visit[0] = true;
for (int i = 0; i < size; i++) {
distances[i] = graph[0][i];
}
int sum = 0;
for (int i = 1; i < size; i++) {
int minPath = Integer.MAX_VALUE;
int minIndex = -1;
for (int j = 0; j < size; j++) {
if (!visit[j] && distances[j] < minPath) {
minPath = distances[j];
minIndex = j;
}
}
if (minIndex == -1) {
return sum;
}
visit[minIndex] = true;
sum += minPath;
for (int j = 0; j < size; j++) {
if (!visit[j] && distances[j] > graph[minIndex][j]) {
distances[j] = graph[minIndex][j];
}
}
}
return sum;
}
public static void main(String[] args) {
System.out.println("hello world!");
}
}