思想:先随便加入图中的某一个节点和其所连通的边,把节点放在一个哈希集合中,边放入以权重为大小比较对象的最小堆中。
然后每次都从最小堆中弹出一个边,我们考察这条边的所连接的to 节点 是否已经被访问过了,
1。如果已经访问过,这条边我们就不要了,因为要了的话 ,要么是该边又重新被考虑,要么该边的加入会导致环。
2. 如果该节点没有被访问过,那说明这条边是没问题的,我们直接加入这条边进入解集,然后把To节点表示为已访问(放入哈希集合中),然后再把to节点所连通的所有边都放入最小堆中。
算法结束。
//primMST
unordered_set<Edge, EdgeHash, Equal_Edge> primMST(Graph graph){
//装边的最小堆
priority_queue<Edge,vector<Edge>,greater<Edge> > small_queue;
//判断节点是否访问过了
unordered_set<Node,NodeHash,Equal_Node> node_set;
unordered_set<Edge,EdgeHash,Equal_Edge> result;
//解决森林的 情况 ,否则只要内层的if就够了
for(auto ite: graph.nodes){
//对每一个节点,都问一下是否访问过了,如果没放过过,做以下操作
if(node_set.find(*ite.second) == node_set.end()){
//把该节点表示为访问过了,再把该节点所解锁的边都加入最小堆中
node_set.insert(*ite.second);
for(Edge* edge: ite.second->edges){
small_queue.push(*edge);
}
//在当前这个图中,取找最小生成树
while(small_queue.size() != 0){
//从最小堆中拿出一个最小权重边,并取得这条边所对应的节点
Edge help_edge = small_queue.top();
small_queue.pop();
Node edge_to = *(help_edge.to);
//判断这个节点是否已经被访问过了,如果没有,则这条边就加入解集,并且我们 认可该点现在被访问了,把该点所有连接的边都加入最小堆
if( node_set.find(edge_to) == node_set.end()){
result.insert(help_edge);
node_set.insert(edge_to);
for(Edge *newEdge : edge_to.edges){
small_queue.push(*newEdge);
}
}
}
}
}
return result;
}
全部代码:
#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_Edge 告知容器当出现hash冲突的时候,如何区分hash值相同的不同对象
struct Equal_Edge {
bool operator()(const Edge& e1, const Edge& e2) const{
return e1.weight == e2.weight && e1.for_hash == e2.for_hash;
}
};
struct Equal_Node {
bool operator()(const Node& n1, const Node& n2) const{
return n1.value == n2.value;
}
};
//primMST
unordered_set<Edge, EdgeHash, Equal_Edge> primMST(Graph graph){
//装边的最小堆
priority_queue<Edge,vector<Edge>,greater<Edge> > small_queue;
//判断节点是否访问过了
unordered_set<Node,NodeHash,Equal_Node> node_set;
unordered_set<Edge,EdgeHash,Equal_Edge> result;
//解决森林的 情况 ,否则只要内层的if就够了
for(auto ite: graph.nodes){
//对每一个节点,都问一下是否访问过了,如果没放过过,做以下操作
if(node_set.find(*ite.second) == node_set.end()){
//把该节点表示为访问过了,再把该节点所解锁的边都加入最小堆中
node_set.insert(*ite.second);
for(Edge* edge: ite.second->edges){
small_queue.push(*edge);
}
//在当前这个图中,取找最小生成树
while(small_queue.size() != 0){
//从最小堆中拿出一个最小权重边,并取得这条边所对应的节点
Edge help_edge = small_queue.top();
small_queue.pop();
Node edge_to = *(help_edge.to);
//判断这个节点是否已经被访问过了,如果没有,则这条边就加入解集,并且我们 认可该点现在被访问了,把该点所有连接的边都加入最小堆
if( node_set.find(edge_to) == node_set.end()){
result.insert(help_edge);
node_set.insert(edge_to);
for(Edge *newEdge : edge_to.edges){
small_queue.push(*newEdge);
}
}
}
}
}
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_Edge> edge_set = primMST(graph);
unordered_set<Edge,EdgeHash,Equal_Edge>::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;
}