prim算法与kruskal算法学习与分析

最小生成树

程序目录结构

DenseGraph.h是邻接矩阵的类

Edge.h是边的类,成员有两个端点与边的权重

Kruskal是实现kruskal的类

lazyPrimMST是惰性prim算法的类

PrimMST是prim算法的类

MinHeap是最小堆的类,用于实现获取最小边

IndexMinHeap是最小索引堆的类,用于实现获取最小边,并且淘汰不需要的边

UnionFind5是并查集的类,用于判环

Prim算法

思想:每次选出与选中点的集合相连的最短的边,并将其所连的点加入选中点的集合

lazyprim

核心代码

#ifndef INC_03_LAZY_PRIM_LAZYPRIMMST_H
#define INC_03_LAZY_PRIM_LAZYPRIMMST_H

#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
#include "MinHeap.h"

using namespace std;

// 使用Prim算法求图的最小生成树
template<typename Graph, typename Weight>
class LazyPrimMST
{

private:
    Graph &G;
    MinHeap<Edge<Weight> > pq;
    bool *marked;
    vector<Edge<Weight> > mst;//存放最小生成树的边
    Weight mstWeight;

    void visit(int v)
    {
        assert(!marked[v]);
        marked[v] = true;

        typename Graph::adjIterator adj(G,v);
        //遍历与v相连的所有边
        for(Edge<Weight>* e=adj.begin(); !adj.end(); e=adj.next())
        {
            //若这条边的另一端未访问则放入最小堆
            if(!marked[e->other(v)])
                pq.insert(*e);
        }
    }
public:
    LazyPrimMST(Graph &graph):G(graph),pq(MinHeap<Edge<Weight> >(graph.E()))
    {
        marked = new bool[G.V()];
        for(int i = 0; i < G.V(); i++)
            marked[i]=false;
        mst.clear();

        visit(0);
        //LazyPrim的时间复杂度为O(ElogE)级别
        while( !pq.isEmpty())
        {
            Edge<Weight> e = pq.extractMin();
            if(marked[e.v()] == marked[e.w()])
            {
                continue;
            }

            mst.push_back(e);
            if(!marked[e.v()])
            {
                visit(e.v());
            }
            else
            {
                visit(e.w());
            }
        }
        mstWeight = mst[0].wt();
        for(int i=1; i<mst.size(); i++)
            mstWeight+=mst[i].wt();
    }
    ~LazyPrimMST()
    {
        delete[] marked;
    }
    vector<Edge<Weight> > mstEdges()
    {
        return mst;
    }

    Weight result()
    {
        return mstWeight;
    }
};
#endif

lazyprim的问题

当一个未选中的点与已选中的点的集合有多条边相连时,会将所有的边都加入最小堆中,虽然当每次取出时取出最小的边,并跳过两端都被选中的边,这样可以保证结果正确,但与此同时也使每次的堆排操作产生不必要的耗时,复杂度为O(ELOGE).

E为边数

优化Prim算法

思路:利用最小索引堆,在插入最小堆时只是存储未选点与已选点集合之间的最短边,将复杂度降为O(ELOGV),V为点数

核心代码

#ifndef INC_05_IMPLEMENTATION_OF_OPTIMIZED_PRIM_ALGORITHM_PRIMMST_H
#define INC_05_IMPLEMENTATION_OF_OPTIMIZED_PRIM_ALGORITHM_PRIMMST_H

#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
#include "IndexMinHeap.h"

using namespace std;

// 使用优化的Prim算法求图的最小生成树
template<typename Graph, typename Weight>
class PrimMST
{
private:
    Graph &G;   //图的引用
    IndexMinHeap<Weight> ipq;   //最小索引堆,算法辅助数据结构
    vector<Edge<Weight>* > edgeTo; //访问的点所对应的边
    bool* marked;   //标记数组
    vector<Edge<Weight> > mst;  //最小生成树所包含的所有边
    Weight mstWeight;   //最小生成树的权值

    //访问节点v
    void visit(int v)
    {
        assert(!marked[v]);
        marked[v] = true;

        typename Graph::adjIterator adj(G,v);
        for(Edge<Weight>* e = adj.begin(); !adj.end(); e=adj.next())
        {
            int w = e->other(v);
            if(!marked[w])
            {
                if(!edgeTo[w])
                {
                    ipq.insert(w,e->wt());
                    edgeTo[w] = e;
                }
                else if(e->wt() < edgeTo[w]->wt())
                {
                    edgeTo[w] = e;
                    ipq.change(w,e->wt());
                }
            }
        }
    }
public:
    PrimMST(Graph &graph):G(graph),ipq(IndexMinHeap<double>(graph.V()))
    {
        marked = new bool[G.V()];
        for(int i=0; i<G.V(); i++)
        {
            marked[i] = false;
            edgeTo.push_back(NULL);
        }
        mst.clear();

        //prim
        visit(0);
        while(!ipq.isEmpty())
        {
            int v = ipq.extractMinIndex();
            assert(edgeTo[v]);
            mst.push_back(*edgeTo[v]);
            visit(v);
        }

        mstWeight = mst[0].wt();
        for(int i = 1; i<mst.size(); i++)
            mstWeight += mst[i].wt();
    }

    ~PrimMST()
    {
        delete[] marked;
    }

    vector<Edge<Weight> > mstEdges()
    {
        return mst;
    }

    Weight result()
    {
        return mstWeight;
    }
};
#endif // INC_05_IMPLEMENTATION_OF_OPTIMIZED_PRIM_ALGORITHM_PRIMMST_H

KRUSKAL算法

思路:每次找出图中最短且不成环的边

核心代码

#ifndef INC_03_KRUSKAL_H
#define INC_03_KRUSKAL_H
#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
#include "MinHeap.h"
#include "UnionFind5.h"

using namespace std;

// Kruskal算法
template <typename Graph, typename Weight>
class KruskalMST{

private:
    vector<Edge<Weight> > mst;   // 最小生成树所包含的所有边
    Weight mstWeight;           // 最小生成树的权值

public:
    // 构造函数, 使用Kruskal算法计算graph的最小生成树
    KruskalMST(Graph &graph){

        // 将图中的所有边存放到一个最小堆中
        MinHeap<Edge<Weight> > pq( graph.E() );
        for( int i = 0 ; i < graph.V() ; i ++ ){
            typename Graph::adjIterator adj(graph,i);
            for( Edge<Weight> *e = adj.begin() ; !adj.end() ; e = adj.next() )
                if( e->v() < e->w() )
                    pq.insert(*e);
        }

        // 创建一个并查集, 来查看已经访问的节点的联通情况
        UnionFind uf = UnionFind(graph.V());
        while(!pq.isEmpty()&&mst.size()<graph.V()-1){
            Edge<Weight> e = pq.extractMin();
            //利用判断根节点是否相同来判断是否成环
            if(uf.isConnected(e.v(),e.w()))
                continue;
            mst.push_back(e);
            //连接两个点
            uf.unionElements(e.v(),e.w());

        }
        mstWeight = mst[0].wt();
        for( int i = 1 ; i < mst.size() ; i ++ )
            mstWeight += mst[i].wt();
    }

    ~KruskalMST(){ }

    // 返回最小生成树的所有边
    vector<Edge<Weight> > mstEdges(){
        return mst;
    };

    // 返回最小生成树的权值
    Weight result(){
        return mstWeight;
    };
};


#endif

测试

主程序

循环生成节点数为100到2000的全通图

#include <iostream>
#include <iomanip>
#include<fstream>
#include "DenseGraph.h"
#include "ReaderGraph.h"
#include "LazyPrimMST.h"
#include "PrimMST.h"
#include "Kruskal.h"
#include<stdio.h>

using namespace std;

// 测试有权图和有权图的读取
int main()
{

    string filename = "testG1.txt";
    double starttime,endtime;
    double time1 = 0.0;
//    ofstream out("data.txt",ios::app);
    ofstream out("data1.txt",ios::app);
//    ofstream out("data2.txt",ios::app);
    for(int V = 100; V<=2000; V+=100)
    {
        DenseGraph<double> g1 = DenseGraph<double>(V, false,true);
//
//        // Test Lazy Prim MST
        cout<<"Test Lazy Prim MST——V:"<<V<<endl;
        starttime=clock();
        LazyPrimMST<DenseGraph<double>, double> lazyPrimMST1(g1);
        endtime=clock();
        time1=double(endtime - starttime)/CLOCKS_PER_SEC;
        cout<<time1<<'s'<<endl;
//        out<<V<<' '<<time1<<endl;
//        cout<<V<<' '<<time1<<endl;
        cout<<"The MST weight is: "<<lazyPrimMST1.result()<<endl;

        cout<<"Test Prim MST——V:"<<V<<endl;
        starttime=clock();
        PrimMST<DenseGraph<double>, double> primMST(g1);
        endtime=clock();
        time1=double(endtime - starttime)/CLOCKS_PER_SEC;
//        out<<V<<' '<<time1<<endl;
        cout<<time1<<'s'<<endl;
        cout<<"The MST weight is: "<<primMST.result()<<endl;

        cout<<"Test Kruskal MST——V:"<<V<<endl;
        starttime=clock();
        KruskalMST<DenseGraph<double>, double> kruskalMST(g1);
        endtime=clock();
        time1=double(endtime - starttime)/CLOCKS_PER_SEC;
//        out<<time1<<endl;
        cout<<time1<<'s'<<endl;
        cout<<"The MST weight is: "<<kruskalMST.result()<<endl;
    }
//    out.close();
    cout<<endl;
    return 0;
}

结果

总结

无论是prim算法还是kruskal算法都是将寻找最小生成树的方法差分为每次寻找最短边的方法,获得局部最优解,最后构成最小树。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值