左神基础课 - 克鲁斯卡尔算法-生成最小生成树

本文介绍了一种基于图结构寻找最小生成树的克鲁斯卡尔算法实现。通过使用并查集和优先队列来高效地选取构成最小生成树的边集。文章详细展示了算法的具体步骤,包括图的构建、并查集的初始化、边的选择过程以及最终结果的输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

输入: 一个图结构

输出: 最小生成树所在的边集

函数内部的逻辑:

1.迭代图中的所有顶点,把他们交给并查集。 由于定义时,并查集的输入定义为结构体类型数组,并非指针数组,所以我们把所有节点 转成成 结构体数组的形式,交给并查集进行构造。

2.并查集得到所有节点元素, 使每个节点的父指针(用hash map表示)指向自己,且陵每个元素 size =1

3.把节点中所有的边都放入小顶堆中,此时记在 边节点的定义 中 重载  边节点的大小对比函数。

4.重最小堆中弹出最小的边后,如果 这个最小边所在的两个节点并不在一个集合中(用并查集判断),则这个边是为最小生成树所要的边,否则舍弃


unordered_set<Edge,EdgeHash,Equal_to> kruskalMST(Graph graph){
	UnionFindSet ufset;
	int length = graph.nodes.size();
	Node node[length];
	int i =0;
	//构造好用于给并查集输入的点集
	unordered_map<int, Node*>::iterator ite = graph.nodes.begin();
	while(ite != graph.nodes.end()){
		node[i] = *(ite->second);
		ite++;
		i++;
	}
	//初始化并查集
	ufset.setUnionFindSet(node, length);
	priority_queue<Edge,vector<Edge>,greater<Edge> > small_queue;
	for(auto edge : graph.edges){
		small_queue.push(*edge);
	}
	//构造选中的输出边集
	unordered_set<Edge,EdgeHash,Equal_to> result;
	while(!small_queue.empty()){
		Edge edge = small_queue.top();
		small_queue.pop();
		if(!ufset.isSameSet(*(edge.from), *(edge.to))){
			result.insert(edge);
			ufset.union_(*(edge.from), *(edge.to));
		}
	}
	cout << "result size : " << result.size()<<endl;
	return result;
}

 

 

 

整体代码:

定义图结构、定义并查集结构、给定图的数组形式的输入,建立一张图,

把该图交给 KrukalMST 函数,得到最终的边集。

 

#include<iostream>
#include<list>
#include<queue>
#include<stack>
#include<unordered_map>
#include<unordered_set>
using namespace std;
//解依赖
class Edge;
//并查集和 图公用一个Node
class Node{
public:
	int value;
	int in;
	int out;
	list<Node*> next;
	list<Edge*> edges;
	//Node 和 Edge 中都重新定义了 ==操作,是因为进入哈希表的,不仅要计算其哈希值,还要计算两节点是否相等
	//在并查集的 union_函数中用到了Node类型的比较操作,所以得自己重新定义
	bool operator == (const Node& n) const{
		return value == n.value;
	}
	//并查集中 father 要和 node节点比较 该node节点是否为代表节点
	bool operator != (const Node& n) const{
		return value != n.value;
	}
	Node(){}
	Node(int value){
		this->value = value;
		in = 0;
		out =0;
	}
};
class Edge{
public:
	int weight;
	int for_hash;
	Node* from;
	Node* to;

	Edge(int weight,Node* from, Node* to){
		this->weight = weight;
		this->from = from;
		this->to = to;
		this->for_hash = to->value;
	}
	bool operator < (const Edge& edge) const{
		return weight < edge.weight;
	}
	bool operator > (const Edge& edge) const{
		return weight > edge.weight;
	}
	bool operator == (const Edge& e) const{
		return weight == e.weight;
	}
};
class Graph{
public:
	unordered_map<int, Node*> nodes;
	unordered_set<Edge*> edges;
};

class GraphGenerator{
public:
	Graph createGraph(int matrix[][3],int rows, int col){
		Graph graph;
		for(int i=0;i<rows;i++){
			int weight = matrix[i][0];
			int from = matrix[i][1];
			int to = matrix[i][2];
			if(graph.nodes.find(from) == graph.nodes.end()){
				graph.nodes[from] = new Node(from);
			}
			if(graph.nodes.find(to) == graph.nodes.end()){
				graph.nodes[to] = new Node(to);
			}
			//以上两个if操作后,必能找到 from 好人to节点
			Node* fromNode = graph.nodes.find(from)->second;
			Node* toNode = graph.nodes.find(to)->second;
			//为 graph 和 from所在的node 准备 一条边
			Edge* newEdge = new Edge(weight, fromNode, toNode);
			//对于新增的一条边, 被指向节点的入度+1
			toNode->in++;
			//对于新增的一条边, 指向节点的出度+1,所指向的节点确定,指向该节点的边确定
			fromNode->out++;
			fromNode->next.push_back(toNode);
			fromNode->edges.push_back(newEdge);
			//两个if会保证建立节点,这里保证 边的存在。
			graph.edges.insert(newEdge);

		}
		return graph;
	}
};
//由于使用了unoredred_map,以前我们使用基础的数据类型,系统能自己计算基础类型的hash值是什么,
//但现在我们要在哈希表中使用自定义的类型,所以得告诉哈希表该如何计算这一类型的哈希值
struct NodeHash{
	size_t operator () (const Node& n) const{
		return hash<int>()(n.value);
	}
};
struct EdgeHash{
	size_t operator () (const Edge& e) const{
		return ( hash<int>()(e.weight) <<1) ^ (hash<int>()(e.for_hash) << 1);
	}
};
//unordered_map/set是采用hash散列进行存储的,因此存储的对象必须提供两个方法,
//1,hash告知此容器如何生成hash的值,
//2,equal_to 告知容器当出现hash冲突的时候,如何区分hash值相同的不同对象
struct Equal_to {
        bool operator()(const Edge& e1, const Edge& e2) const{
            return e1.weight == e2.weight && e1.for_hash == e2.for_hash;
        }
};

class UnionFindSet{
private:
	unordered_map<Node, Node, NodeHash> fatherMap;
	unordered_map<Node, int,NodeHash> sizeMap;
public:
	//一开始,就要给我们所有的集合中的元素,然后让它们各自是一个集合,然后设每个集合的size为1
	void setUnionFindSet(Node nodes[], int length){
		for(int i=0; i<length; i++){
			fatherMap[nodes[i]] = nodes[i];
			sizeMap[nodes[i]] = 1;
		}
	}
	//往上 用递归帮忙找代表节点
	Node findDaiBiao(Node node){
		Node father = fatherMap.find(node)->second;
		if(father != node){
			father = findDaiBiao(father);
		}
		fatherMap[node] = father;
		return father;
	}
	//借助 找到的代表节点,判断两个节点是否在一个集合中
	bool isSameSet(Node n1, Node n2){
		Node daibiao_n1 = findDaiBiao(n1);
		Node daibiao_n2 = findDaiBiao(n2);
		if(daibiao_n1 != daibiao_n2)
			return false;
		return true;
	}
	//如果两个节点不在同一个集合里,那么我们就要把他们合并起来,size小的集合的代表节点,
	//其parentmap中value改成size大的代表节点,更新size大的集合的size
	void union_(Node n1, Node n2){
		Node daibiao_n1 = findDaiBiao(n1);
		Node daibiao_n2 = findDaiBiao(n2);
		if(daibiao_n1 != daibiao_n2){
			int size1 = sizeMap.find(daibiao_n1)->second;
			int size2 = sizeMap.find(daibiao_n2)->second;
			if(size1 < size2){
				fatherMap[daibiao_n1]=daibiao_n2;
				sizeMap[daibiao_n2]=(size1 + size2);
			} else{
				fatherMap[daibiao_n2]=daibiao_n1;
				sizeMap[daibiao_n1]=(size1+size2);
			}
		}
	}
};

unordered_set<Edge,EdgeHash,Equal_to> kruskalMST(Graph graph){
	UnionFindSet ufset;
	int length = graph.nodes.size();
	Node node[length];
	int i =0;
	//构造好用于给并查集输入的点集
	unordered_map<int, Node*>::iterator ite = graph.nodes.begin();
	while(ite != graph.nodes.end()){
		node[i] = *(ite->second);
		ite++;
		i++;
	}
	//初始化并查集
	ufset.setUnionFindSet(node, length);
	priority_queue<Edge,vector<Edge>,greater<Edge> > small_queue;
	for(auto edge : graph.edges){
		small_queue.push(*edge);
	}
	//构造选中的输出边集
	unordered_set<Edge,EdgeHash,Equal_to> result;
	while(!small_queue.empty()){
		Edge edge = small_queue.top();
		small_queue.pop();
		if(!ufset.isSameSet(*(edge.from), *(edge.to))){
			result.insert(edge);
			ufset.union_(*(edge.from), *(edge.to));
		}
	}
	cout << "result size : " << result.size()<<endl;
	return result;
}

int main(){
	GraphGenerator g;
	int matrix[][3]={{1,1,2},{1,1,3},{2,1,4},{2,2,3},{3,2,7},{4,7,3},
				{5,3,5},{6,4,6}};
	int length = sizeof(matrix)/sizeof(matrix[0]);
	Graph graph = g.createGraph(matrix, length,3);
	unordered_set<Edge,EdgeHash,Equal_to> edge_set = kruskalMST(graph);
	unordered_set<Edge,EdgeHash,Equal_to>::iterator ite2  = edge_set.begin();
	while(ite2 != edge_set.end()){
		cout<< "from value: "<<(*ite2).from->value<<"  ";
		cout<< "to value: "<<(*ite2).to->value<<" ";
		cout << "weight : " << (*ite2).weight << endl;
		ite2++;
	}
	return 0;
}	

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值