最小生成树(Prim算法,Kruskal算法 )

声明:图片及内容基于https://www.bilibili.com/video/BV1yp4y1Q74o?from=articleDetail

最小生成树原理

普利姆(Prim)算法

原理

Prim算法的实现

 

 核心代码

void MGraph::Prim(int start){
    shortEdge shortEdge;                     //建立shortEdge数组 
    for(int i=0;i<vertexNum;i++){           
        shortEdge[i].lowcost=arc[start][i]; // 数组初始化,lowcost为邻接矩阵第i行的权值 
        shortEdge[i].adjvex=start;          //adjvex初始化为起始值 
    }
    shortEdge[start].lowcost=0;;            //lowcost为0,把start放入集合 
    
    for(int i=0;i<vertexNum-1;i++){
        int k=minEdge(shortEdge,vertexNum); //求出最小权值下标 
        outputSMT(k,shortEdge[k]);
        shortEdge[k].lowcost=0;             //k放入集合 
        for(int j=0;j<vertexNum;j++){           //更新shortEdge数组,当前集合和刚进入集合的结点的权值比较 
            if(arc[k][j]<shortEdge[j].lowcost){
                shortEdge[j].lowcost=arc[k][j];
                shortEdge[j].adjvex=k;
            }
        }
    }
}
int minEdge(shortEdge shortEdge,int vertexNum){  //求shortEdge数组中最小权值的下标 
    Edge e;                   //e是个临时变量用来记录当前小权值的下标和权值 
    e.lowcost=INFINIT;
    e.adjvex=-1;
    int i;
    for(i=0;i<vertexNum;i++){             //权值不为0(已经在集合中),不为无穷 (无路径) 
        if(shortEdge[i].lowcost!=0&&shortEdge[i].lowcost!=INFINIT&&e.lowcost>shortEdge[i].lowcost){
            e.lowcost=shortEdge[i].lowcost;
            e.adjvex=i;
        }
            
    }
    return e.adjvex;
}
void outputSMT(int k,Edge Edge){            //打印路径和权值 
    cout<<"("<<Edge.adjvex<<","<<k<<") "<<Edge.lowcost<<endl;
}

完整代码

#include<iostream>
#define MAXVEX 100
using namespace std;
const int INFINIT=65535;
int visit[MAXVEX];
typedef struct Edge{
    int lowcost;
    int adjvex;
}Edge,shortEdge[MAXVEX];

class MGraph {
    private:
        int *vertex;         //顶点信息
        int **arc;         //邻接矩阵
        int vertexNum,arcNum;        //顶点数,边数
    public:
        MGraph(int v[],int n,int e);
        ~MGraph();
        void display();
        void Prim(int start);
};


MGraph::MGraph(int v[],int n,int e) {  //n是顶点数,e是边数
    vertexNum=n;
    arcNum=e;
    vertex = new int[vertexNum];         //建立顶点信息
    arc = new int*[vertexNum];         //建立邻接表
    for(int i=0; i<vertexNum; i++)
        arc[i]=new int[vertexNum];

    for(int i=0; i<vertexNum; i++) {   //初始化顶点信息 
        vertex[i]=v[i];
    }
    for(int i=0;i<vertexNum;i++)       //初始化邻接表 
        for(int j=0;j<vertexNum;j++){
            if(i==j)     arc[i][j]=0;
            else     arc[i][j]=INFINIT;
        }
            
    int vi,vj,w;
    for(int i=0;i<arcNum;i++){
        cout<<"请输入边的两个顶点和这条边的权值"<<endl; 
        cin>>vi>>vj>>w;   //输入边依附的两个顶点的编号 和权值 
        arc[vi][vj]=w; //有边标志 
        arc[vj][vi]=w;
    }
}


void MGraph::display(){
    for(int i=0;i<vertexNum;i++){
        for(int j=0;j<vertexNum;j++){
            if(arc[i][j]==INFINIT)
                cout<<""<<"\t"; 
            else cout<<arc[i][j]<<"\t";
        }
        cout<<endl;
    }
    cout<<endl;

    for(int i=0;i<vertexNum;i++){
        cout<<vertex[i]<<" ";
    }
    cout<<endl;
}


MGraph::~MGraph(){
    delete []vertex;
    for(int i=0;i<vertexNum;i++)
        delete [] arc[i];
    delete [] arc;
}
int minEdge(shortEdge shortEdge,int vertexNum){  //求shortEdge数组中最小权值的下标 
    Edge e;                   //e是个临时变量用来记录当前小权值的下标和权值 
    e.lowcost=INFINIT;
    e.adjvex=-1;
    int i;
    for(i=0;i<vertexNum;i++){             //权值不为0(已经在集合中),不为无穷 (无路径) 
        if(shortEdge[i].lowcost!=0&&shortEdge[i].lowcost!=INFINIT&&e.lowcost>shortEdge[i].lowcost){
            e.lowcost=shortEdge[i].lowcost;
            e.adjvex=i;
        }
            
    }
    return e.adjvex;
}
void outputSMT(int k,Edge Edge){            //打印路径和权值 
    cout<<"("<<Edge.adjvex<<","<<k<<") "<<Edge.lowcost<<endl;
}
void MGraph::Prim(int start){
    shortEdge shortEdge;                     //建立shortEdge数组 
    for(int i=0;i<vertexNum;i++){           
        shortEdge[i].lowcost=arc[start][i]; // 数组初始化,lowcost为邻接矩阵第i行的权值 
        shortEdge[i].adjvex=start;          //adjvex初始化为起始值 
    }
    shortEdge[start].lowcost=0;;            //lowcost为0,把start放入集合 
    
    for(int i=0;i<vertexNum-1;i++){          //注意:i<vectexNum-1 
        int k=minEdge(shortEdge,vertexNum); //求出最小权值下标 
        outputSMT(k,shortEdge[k]);
        shortEdge[k].lowcost=0;             //k放入集合 
        for(int j=0;j<vertexNum;j++){           //更新shortEdge数组,当前集合和刚进入集合的结点的权值比较 
            if(arc[k][j]<shortEdge[j].lowcost){
                shortEdge[j].lowcost=arc[k][j];
                shortEdge[j].adjvex=k;
            }
        }
    }
}

int main(){
    int v[6]={0,1,2,3,4,5};
    cout<<"请输入顶点个数和边的个数"<<endl;
    int m,n;
    cin>>m>>n;
    cout<<"请输入prim算法的起点"<<endl; 
    int k;
    cin>>k;
    
    MGraph mgraph(v,m,n);
    cout<<"输出邻接矩阵信息和边数组信息:"<<endl;
    mgraph.display();
    cout<<"输出起点从"<<k<<"开始的最小生成树:" <<endl;
    mgraph.Prim(k);    
    return 0;
}

 

输入:

6 9

3

0 1 34
0 2 46
0 5 19
1 4 12
2 3 17
2 5 25
3 5 25
3 4 38
4 5 26

输出:

输出邻接矩阵信息和边数组信息:
0  34  46  ∞  ∞  19
34 0   ∞   ∞  12  ∞
46 ∞   0   17 ∞  25
∞  ∞   17  0  38 25
∞  12  ∞  38  0  26
19 ∞   25 25 26  0

0 1 2 3 4 5
输出起点从3开始的最小生成树:
(3,2) 17
(3,5) 25
(5,0) 19
(5,4) 26
(4,1) 12

克鲁斯卡尔(Kruskal)算法

原理

Kruskal算法的实现

核心代码

void EdgeGraph::Kruskal(){
    for(int i=0;i<vertexNum;i++){  //parent数组初始化 
        parent[i]=-1;
    }
    sortEdge();                    //边集排序 
    int vex1,vex2,num=0;
    for(int i=0;i<edgeNum;i++){
        vex1=findRoot(edge[i].from); //找到所在生成树的根节点 
        vex2=findRoot(edge[i].to);   //找到所在生成树的根节点 
        if(vex1!=vex2){          //找到两个根节点不相同,不会构成环 
            outputMST(edge[i]);    //打印 
            parent[vex2]=vex1; //合并生成树
            num++;
        if(num==vertexNum-1) return;   //循环vetexNum-1次,提前返回 
        }
    } 
}
int EdgeGraph::findRoot(int v){      //寻找根节点 
    int t=v; 
    while(parent[t]>-1){
        t=parent[t];
    }
    return t;
}

完整代码

#include<iostream>
#define dataType int
using namespace std;
const int MaxVertex=10;
const int MaxEdge=100;
struct EdgeType{
    int from,to;
    int weight;
}; 
class EdgeGraph{
    private:
        dataType vertex[MaxVertex];
        EdgeType edge[MaxEdge];
        int vertexNum,edgeNum;
        int parent[MaxVertex];
    public:
        EdgeGraph(int n,int e,dataType v[]);
        int findRoot(int v);
        void Kruskal();
        void outputMST(EdgeType edge);
        void sortEdge(); 
};
EdgeGraph::EdgeGraph(int n,int e,dataType v[]){
    vertexNum=n;  //顶点数 
    edgeNum=e;    //边数
    for(int i=0;i<vertexNum;i++){
        vertex[i]=v[i];
    }
    int start,end,w;
    for(int i=0;i<e;i++){
        cout<<"请输入第"<<i+1<<"条边的两个邻接点和权值"<<endl;
        cin>>start>>end>>w;
        edge[i].from=start;
        edge[i].to=end;
        edge[i].weight=w;
    }
}
void EdgeGraph::Kruskal(){
    for(int i=0;i<vertexNum;i++){  //parent数组初始化 
        parent[i]=-1;
    }
    sortEdge();                    //边集排序 
    int vex1,vex2,num=0;
    for(int i=0;i<edgeNum;i++){
        vex1=findRoot(edge[i].from); //找到所在生成树的根节点 
        vex2=findRoot(edge[i].to);   //找到所在生成树的根节点 
        if(vex1!=vex2){          //找到两个根节点不相同,不会构成环 
            outputMST(edge[i]);    //打印 
            parent[vex2]=vex1; //合并生成树
            num++;
        if(num==vertexNum-1) return;   //循环vetexNum-1次,提前返回 
        }
    } 
}
void EdgeGraph::sortEdge(){
    bool flag=true;
    while(flag){                  //优化版冒泡排序 
        flag=false;
        for(int i=0;i<edgeNum-1;i++){
            for(int j=i+1;j<edgeNum;j++){
                if(edge[i].weight>edge[j].weight){
                    flag=true;
                    EdgeType t=edge[i];
                    edge[i]=edge[j];
                    edge[j]=t;
                }
            }
        }
    } 
}
int EdgeGraph::findRoot(int v){      //寻找根节点 
    int t=v; 
    while(parent[t]>-1){
        t=parent[t];
    }
    return t;
}
void EdgeGraph::outputMST(EdgeType edge){
    cout<<"("<<edge.from<<","<<edge.to<<") "<<edge.weight<<endl;
} 
int main(){
    cout<<"请输入结点数和边数"<<endl;
    int n,e;
    cin>>n>>e;
    int v[MaxEdge];
    cout<<"请输入"<<n<<"个结点信息"<<endl; 
    for(int i=0;i<n;i++)
        cin>>v[i];
    EdgeGraph edgegraph(n,e,v);
    edgegraph.Kruskal();
    return 0;
}

输入:

6 9

0 1 2 3 4 5

1 4 12
2 3 17
0 5 19
2 5 25
3 5 25
4 5 26
0 1 34
3 4 38
0 2 46

输出:

(1,4) 12
(2,3) 17
(0,5) 19
(2,5) 25
(4,5) 26

posted @ 2021-03-31 08:40  gonghr  阅读(2443)  评论(1编辑  收藏  举报