代码实现: 最小生成树问题--Prim算法&Kruskal算法

Prim 算法:

#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> visited;
    unordered_set<Edge, EdgeHash, Equal_Edge> result;
    //解决森林的 情况 ,否则只要内层的if就够了
    unordered_map<int, Node*>::iterator ite = graph.nodes.begin();
    unordered_set<Edge, EdgeHash, Equal_Edge> ret;

    while (ite != graph.nodes.end()) 
    {
        if (visited.find(*(ite->second)) == visited.end()) 
        {
            visited.insert(*(ite->second));
            for (auto edge : ite->second->edges) {
                small_queue.push(*(edge));
            }

            while (!small_queue.empty()) {
                auto temp = small_queue.top();
                small_queue.pop();
                if (visited.find(*(temp.to)) == visited.end()) {
                    visited.insert(*(temp.to));
                    ret.insert(temp);
                }
                for (auto edge : temp.to->edges) {
                    small_queue.push(*(edge));
                }
            }
        }

        return ret;
    }
        //对每一个节点,都问一下是否访问过了,如果没放过过,做以下操作
        
            //把该节点表示为访问过了,再把该节点所解锁的边都加入最小堆中
            


            //在当前这个图中,取找最小生成树
            
                //从最小堆中拿出一个最小权重边,并取得这条边所对应的节点
                


                //判断这个节点是否已经被访问过了,如果没有,则这条边就加入解集,并且我们 认可该点现在被访问了,把该点所有连接的边都加入最小堆
                
        
    
    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;
}

 

 

 

 Kruskal算法:

#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;
}

 

 

posted @ 2020-12-07 16:36  彭张智写字的地方  阅读(209)  评论(0)    收藏  举报