图相关(三)图的邻接矩阵表示(C++)及最最小生成树算法(prim和kruskal)

一.测试用图

邻接矩阵表示:

//prim注意是无向图
    vector<vector<int>> p(6, vector<int>(6, INF));//类似dijikstra,没有边的点设为INF
    p[0][1] = 10;
    p[0][3] = 30;
    p[0][4] = 45;
    p[1][0] = 10;
    p[3][0] = 30;
    p[4][0] = 45;
 
    p[1][2] = 50;
    p[1][4] = 40;
    p[1][5] = 25;
    p[2][1] = 50;
    p[4][1] = 40;
    p[5][1] = 25;
 
    p[2][4] = 35;
    p[2][5] = 15;
    p[4][2] = 35;
    p[5][2] = 15;
 
    p[3][5] = 20;
    p[5][3] = 20;
 
    p[4][5] = 55;
    p[5][4] = 55;

 

注意:prim算法的图为无向图,和dijikstra一样,没有边的地方要用INF填充

其中INF表示无穷:


const int INF = 0x3f3f3f;//不能取得太大

二.prim算法:

//prim算法,返回最小生成树的权值
int Prim(vector<vector<int>> cost)//下标从0开始
{
    int n = cost.size();
    vector<bool> visited(n, false);
    vector<int> lowc(n, 0);
    int ans = 0;
    visited[0] = true;//第一个点标记为已访问
    for (int i = 1;i < n;i++)lowc[i] = cost[0][i];
    for (int i = 1;i < n;i++)
    {
        int minc = INF;
        int p = -1;
        for (int j = 0;j < n;j++)
            if (!visited[j] && minc > lowc[j])
            {
                minc = lowc[j];
                p = j;
            }
        if (minc == INF)return -1;//原图不连通
        ans += minc;
        visited[p] = true;
        for (int j = 0;j < n;j++)
            if (!visited[j] && lowc[j] > cost[p][j])
                lowc[j] = cost[p][j];
    }
    return ans;
}

 测试:

int minWeight =Prim(p);
    cout << "minWeight= " << minWeight << endl;

测试结果:

 

 三.kruskal算法:

图改为边数组表示,利用并查集很容易理解。此处直接用一个数组来表示并查集,数组的下标表示节点的编号,对应的值为其父指针的值,初始时,将并查集中的所有元素的父指针指向-1.

3.1图的边数组表示法:

//kruskal:使用并查集
struct Edge {
    int from;//起点
    int to;//终点
    int weight;
    bool operator<(const Edge&rhs){//重载operator <之后可以直接调用sort
        return weight < rhs.weight;//此处也可以单独写一个仿函数,传给sort作比较器
    }
};

此时上面的测试用图表示为:

 vector<Edge> kru =
    { { 0,1,10 },{ 1,0,10 },{ 0,3,30 },{ 3,0,30 },{ 0,4,45 },{ 4,0,45 },{ 1,2,50 },{ 2,1,50 },
    { 1,4,40 },{ 4,1,40 },{ 1,5,25 },{ 5,1,25 },{ 2,4,35 },{ 4,2,35 },
    { 3,5,20 },{ 5,3,20 },{ 5,4,55 },{ 4,5,55 },{ 2,5,15 },{ 5,2,15 } };
 

3.2并查集的find函数

int Find(vector<int>& UnionFindSet,int x){//索引表示节点标号,对应的值为其父节点的标号
    if (UnionFindSet[x] == -1)//代表元素是-1,说明只有一个元素
        return x;
    return UnionFindSet[x] = Find(UnionFindSet,UnionFindSet[x]);//递归修改,扁平化
}

3.3 kruskal算法

int kruskal(vector<Edge> graph,int vertexNum) {
    size_t N = graph.size();
    int ans = 0;
    int cnt = 0;//已经选取的边数
    vector<int> UnionFindSet(N, -1);//初始化并查集,每个集合的代表元素置为-1;
    sort(graph.begin(), graph.end());//给边排序,拍好序后边的权值由小到大排列
    for (size_t i = 0;i < N;++i) {
        int from = graph[i].from;
        int to = graph[i].to;
        int weight = graph[i].weight;
        //找出这条边的两个顶点所在的集合
        int t1 = Find(UnionFindSet, from);
        int t2 = Find(UnionFindSet, to);
        if (t1 != t2) {//t1和t2不在一个集合时,即没有形成回路时,选取该边
            ans += weight;
            UnionFindSet[t1] = t2;//合并两个集合
            ++cnt;
        }
        if(cnt==vertexNum-1)//选够顶点数减1条边时,退出,最小生成树的边数为顶点数减一
            break;
    }
    if (cnt < vertexNum - 1) return -1;//不连通
    else return ans;
}

3.4 kruskal测试

cout << "kruskal: " << endl;
    vector<Edge> kru =
    { { 0,1,10 },{ 1,0,10 },{ 0,3,30 },{ 3,0,30 },{ 0,4,45 },{ 4,0,45 },{ 1,2,50 },{ 2,1,50 },
    { 1,4,40 },{ 4,1,40 },{ 1,5,25 },{ 5,1,25 },{ 2,4,35 },{ 4,2,35 },
    { 3,5,20 },{ 5,3,20 },{ 5,4,55 },{ 4,5,55 },{ 2,5,15 },{ 5,2,15 } };
 
    int ret = kruskal(kru, 6);
    cout << "kruskal= " << ret << endl;

3.5 kruskal测试结果

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值