DS图—最小生成树(待验证)

题解
一.prim
  1. prim算法的时间复杂度为O(n^2),与网中的边数无关,适用于求边稠密的网的最小生成树。
  2. prim算法中每次都要找一端在U中一端在V-U中的边中权值最小的边。这里U的实现可以通过设置一个visit数组,如果在U里面就置为1,而不是为U重新创建一个顶点集。找权值最小就可以先设置大初值变量minweight,然后遍历所有符合条件的边,如果权值比minweight小就赋给minweight。
  3. prim循环结束的条件是U等于V,对应到算法就是visit数组全为1。这里visit数组是否全为1不能作为判断条件直接写进while头,所以可以为这段代码单独开一个isOver函数,然后while头中用该函数就好了。
  4. prim要求输出图最小生成树的边信息,所以这里需要对边进行存储。边一共包含三个数据,start顶点,end顶点,边的权值,对其存储可以通过结构体数组、三个普通数组结合、三个队列或三个栈、邻接表或邻接矩阵,前两者的缺点在于存入和输出时需要计数,而用栈和队列就不需要计数,这里采用结构体数组来存(注意startvex和endvex是代表对应顶点的下标,而不是其内容,因为下标操作起来更方便一些)。
  5. 代码中Index函数的作用是根据传入数据的内容,返回其在数组中对应的下标。对于数组而言,知道下标很容易得到数组值,而知道值要得到下标需要遍历整个数组,并且常常用到这个操作,所以干脆给数组定义一个Index函数。
  6. 在网里面邻接矩阵的值不再是0和1,而是将图中的1变为了权值,而在prim中需要选择权值最小的边,如果还是将没有边用0表示的话就会对其产生干扰,所以这里初始化网用无穷。
二.kruskal
  1. kruskal算法的时间复杂度为O(eloge),e为网中的边数,适用于求解边稀疏的网的最小生成树。
  2. kruskal算法中每次需要选择权值最小的边,并且之前选择过的边不参加选择。其的实现可以通过用一个结构体数组来存无向网中的所有边,并且以边权值的升序顺序存储,这样每次就只要向后遍历该数组取边就可以了。实现权值升序可以先正常存,然后针对其权值来个冒泡排序就可以了。
  3. 每次选取了权值最小的边之后需要判断其的start顶点和end顶点是否位于同一连通分量。这里可以将位于同一连通分量的顶点视为一棵树,它们有同一个根节点,因此是否在同一连通分量就转化为顶点的根节点是否相同。根节点的实现可以设置一个parent数组,初始每个顶点的parent都置为-1代表它们都是根节点(由于顶点下标0开始,-1代表为根节点避免与0冲突)。这里需要对顶点进行找根操作,可以定义一个FindRoot函数(不断迭代到parent为-1)。如果start和end顶点根节点不同,就可以输出这条边了(这里最小生成树中的边不需要存储,得到后直接输出就好了),然后让end顶点的parent值为start顶点下标就完成了统一根节点。
  4. 这里结束函数的条件就在于是否找到并输出了vexnum-1条边。可以在对应的程序块中添加一个控制变量,每次执行该段就++,当等于vexnum-1就return退出函数。
题目
问题 A: DS图—最小生成树
时间限制: 1 Sec  内存限制: 128 MB
提交: 1035  解决: 395
[提交][状态][讨论版]
题目描述
根据输入创建无向网。分别用Prim算法和Kruskal算法构建最小生成树。(假设:输入数据的最小生成树唯一。)

输入
顶点数n

n个顶点

边数m

m条边信息,格式为:顶点1 顶点2 权值

Prim算法的起点v

输出
输出最小生成树的权值之和

对两种算法,按树的生长顺序,输出边信息(Kruskal中边顶点按数组序号升序输出)

样例输入
6
v1 v2 v3 v4 v5 v6 
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6
v3 v6 4
v4 v6 2
v5 v6 6
v1
样例输出
15
prim:
v1 v3 1
v3 v6 4
v6 v4 2
v3 v2 5
v2 v5 3
kruskal:
v1 v3 1
v4 v6 2
v2 v5 3
v3 v6 4
v2 v3 5
代码块
#include <iostream>
using namespace std;

class Edge
{
    int weight;
    int startvex, endvex;
public:
    Edge(){}
    ~Edge(){}
    friend class Graph;
};

class Graph
{
private:
    int vexnum;
    int edgenum;
    string *vertex;
    int **matrix;
    bool *visit;
    Edge *edge1;
    int *parent;
    Edge *edge2;
    int Index(string str);
    bool IsOver();
    int FindRoot(int vex);
public:
    Graph();
    ~Graph();
    void MinSpanTree_Prim(string vex);
    void MinSpanTree_Kruskal();
};

Graph::Graph()
{
    int i, j, weight;
    string startvex, endvex;//这里对顶点的表示用string而不是下标是因为输入或输出的形式所迫,其他情况用下标,这种形式想对它操作还要麻烦地用Index函数定位到下标。
    cin>>vexnum;
    vertex = new string[vexnum];
    matrix = new int*[vexnum];
    visit = new bool[vexnum];
    edge1 = new Edge[vexnum-1];
    parent = new int[vexnum];
    for(i=0; i<vexnum; i++)
    {
        cin>>vertex[i];
        matrix[i] = new int[vexnum];
        visit[i] = false;
        parent[i] = -1;
        for(j=0; j<vexnum; j++)
            matrix[i][j] = 99999;//用无穷初始化邻接矩阵
    }
    cin>>edgenum;
    edge2 = new Edge[edgenum];
    for(i=0; i<edgenum; i++)
    {
        cin>>startvex>>endvex>>weight;
        edge2[i].endvex = Index(endvex);
        edge2[i].startvex = Index(startvex);
        edge2[i].weight = weight;
        matrix[Index(startvex)][Index(endvex)] = weight;
        matrix[Index(endvex)][Index(startvex)] = weight;//在无向图中注意要把对称的边也初始化。
    }
    Edge temp;
    for(i=0; i<edgenum-1; i++)
    {//针对结构体中weight进行冒泡排序
        for(j=0; j<edgenum-1-i; j++)
        {
            if(edge2[j].weight>edge2[j+1].weight)
            {
                temp = edge2[j];
                edge2[j] = edge2[j+1];
                edge2[j+1] = temp;
            }
        }
    }
}

Graph::~Graph()
{
    for(int i=0; i<vexnum; i++)
        delete []matrix[i];
    delete []vertex;
    delete []matrix;
    delete []visit;
    delete []edge1;
    delete []parent;
    delete []edge2;
}

int Graph::Index(string str)
{
    for(int i=0; i<vexnum; i++)
    {
        if(vertex[i]==str)
            return i;
    }
}

bool Graph::IsOver()
{
    for(int i=0; i<vexnum; i++)
    {
        if(!visit[i])
            return false;
    }
    return true;
}

int Graph::FindRoot(int vex)
{
    while(parent[vex]!=-1)
    {
        vex = parent[vex];
    }
    return vex;
}

void Graph::MinSpanTree_Prim(string vex)
{
    int i, j, startvex, endvex;
    int weightsum = 0, pos = 0;
    visit[Index(vex)] = true;
    while(!IsOver())
    {
        int minweight = 99999;
        for(i=0; i<vexnum; i++)
        {//遍历所有符合条件的边:先用for对所有边遍历,然后for里面再用if筛选符合条件的情况。
            if(visit[i])
            {
                for(j=0; j<vexnum; j++)
                {
                    if(!visit[j] && matrix[i][j]<minweight)
                    {
                        minweight = matrix[i][j];
                        startvex = i;
                        endvex = j;
                    }
                }
            }
        }
        visit[endvex] = true;
        weightsum += minweight;
        edge1[pos].startvex = startvex;
        edge1[pos].endvex = endvex;
        edge1[pos].weight = minweight;
        pos++;
    }
    cout<<weightsum<<endl;
    cout<<"prim:"<<endl;
    for(i=0; i<vexnum-1; i++)
        cout<<vertex[edge1[i].startvex]<<' '<<vertex[edge1[i].endvex]<<' '<<edge1[i].weight<<endl;//注意这里的startvex和endvex只是下标,输出要套上顶点数组。
}

void Graph::MinSpanTree_Kruskal()
{
    int i, vex1, vex2;
    int num = 0;
    cout<<"kruskal:"<<endl;
    for(i=0; i<edgenum; i++)
    {
        vex1 = FindRoot(edge2[i].startvex);
        vex2 = FindRoot(edge2[i].endvex);
        if(vex1!=vex2)
        {
            cout<<vertex[edge2[i].startvex]<<' '<<vertex[edge2[i].endvex]<<' '<<edge2[i].weight<<endl;
            parent[vex2] = vex1;
            num++;
            if(num==vexnum-1)
                return;
        }
    }
}

int main(void)
{
    Graph myGraph;
    string vex;
    cin>>vex;
    myGraph.MinSpanTree_Prim(vex);
    myGraph.MinSpanTree_Kruskal();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值