Prim算法和Kruskal算法C++代码实现

高级算法分析与设计课程论文实验代码

Prim算法

#include <iostream>
#include <string>
#include <fstream>
#include <windows.h>
#include "MyTimer.h"
using namespace std;

#define MAXV 400 //最大顶点个数

typedef int InfoType;

typedef struct
{
    int no;        //顶点编号
    InfoType info; //顶点其他信息
} VertexType;      //顶点类型

typedef struct //图的定义
{
    VertexType vexs[MAXV]; //存放顶点信息
    int edges[MAXV][MAXV]; //邻接矩阵
    int vexnum;            //顶点数
    int arcnum;            //弧数
} MGraph;                  //图的邻接矩阵类型

//打印图
void print(MGraph g)
{
    int i;
    for (i = 0; i < g.vexnum; i++)
    {
        for (int j = 0; j < g.vexnum; j++)
        {
            if (g.edges[i][j] == INT_MAX)
                cout << "∞"
                     << " ";
            else
                cout << g.edges[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

//作为记录边的信息,这些边都是达到end的所有边中,权重最小的那个
struct Assis_array
{
    int start;  //边的终点
    int end;    //边的起点
    int weight; //边的权重
};
//进行prim算法实现,使用的邻接矩阵的方法实现。
void Prim(MGraph g, int begin)
{
    // close_edge这个数组记录到达某个顶点的各个边中的权重最大的那个边
    Assis_array *close_edge = new Assis_array[g.vexnum];

    int j;

    //进行close_edge的初始化,更加开始起点进行初始化
    for (j = 0; j < g.vexnum; j++)
    {
        if (j != begin - 1)
        {
            close_edge[j].start = begin - 1;
            close_edge[j].end = j;
            close_edge[j].weight = g.edges[begin - 1][j];
        }
    }
    //把起点的close_edge中的值设置为-1,代表已经加入到集合U了
    close_edge[begin - 1].weight = -1;
    //访问剩下的顶点,并加入依次加入到集合U
    for (j = 1; j < g.vexnum; j++)
    {

        int min = INT_MAX;
        int k;
        int index;
        //寻找数组close_edge中权重最小的那个边
        for (k = 0; k < g.vexnum; k++)
        {
            if (close_edge[k].weight != -1)
            {
                if (close_edge[k].weight < min)
                {
                    min = close_edge[k].weight;
                    index = k;
                }
            }
        }
        //将权重最小的那条边的终点也加入到集合U
        close_edge[index].weight = -1;
        //输出对应的边的信息
        cout << "  (" << close_edge[index].start << ","
             << close_edge[index].end << "):"
             << g.edges[close_edge[index].start][close_edge[index].end]
             << endl;

        //更新close_edge数组。
        for (k = 0; k < g.vexnum; k++)
        {
            if (g.edges[close_edge[index].end][k] < close_edge[k].weight)
            {
                close_edge[k].weight = g.edges[close_edge[index].end][k];
                close_edge[k].start = close_edge[index].end;
                close_edge[k].end = k;
            }
        }
    }
}

//创建图
void createGraph(MGraph &g)
{
    int i, j, a;
    // ifstream in("Sparse.txt");
    ifstream in("Dense.txt");
    in >> g.vexnum >> g.arcnum;

    for (i = 0; i < g.vexnum; i++)
    {
        for (j = 0; j < g.vexnum; j++)
        {
            in >> a;
            g.edges[i][j] = a == -1 ? INT_MAX : a;
        }
    }
}

int main()
{
    MGraph g;
    MyTimer mt;
    createGraph(g);
    print(g);
    cout << "最小生成树为:" << endl;
    mt.Start();
    Prim(g, 1);
    mt.End();
    cout << "查找最小生成树用时" << mt.costTime << "微秒。" << endl;
    system("pause");
    return 0;
}

Kruskal算法

#include <iostream>
#include <fstream>
#include <windows.h>
#include "MyTimer.h"
using namespace std;

#define MaxSize 10000
#define MAXV 400 //最大顶点个数
typedef int InfoType;

typedef struct
{
    int no;        //顶点编号
    InfoType info; //顶点其他信息
} VertexType;      //顶点类型

typedef struct //图的定义
{
    VertexType vexs[MAXV]; //存放顶点信息
    int edges[MAXV][MAXV]; //邻接矩阵
    int vexnum;            //顶点数
    int arcnum;            //弧数
} MGraph;                  //图的邻接矩阵类型

typedef struct
{
    int u; //边的起始顶点
    int v; //边的终止顶点
    int w; //边的权值
} Edge;
typedef struct node
{
    int rank;                     //结点对应秩
    int parent;                   //结点对应双亲下标
} UFSTree;                        //并查集树结点类型
void MAKE_SET(UFSTree t[], int n) //初始化并查集树
{
    int i;
    for (i = 0; i < n; i++) //顶点编号从0到n-1
    {
        t[i].rank = 0;   //秩初始化为0
        t[i].parent = i; //双亲初始化指向自已
    }
}
int FIND_SET(UFSTree t[], int x) //在x所在子树中查找集合编号
{
    if (x != t[x].parent)                  //双亲不是自已
        return (FIND_SET(t, t[x].parent)); //递归在双亲中找x
    else
        return (x); //双亲是自已,返回x
}
void UNION(UFSTree t[], int x, int y) //将x和y所在的子树合并
{
    x = FIND_SET(t, x);
    y = FIND_SET(t, y);
    if (t[x].rank > t[y].rank) // y结点的秩小于x结点的秩
        t[y].parent = x;       //将y连到x结点上,x作为y的孩子结点
    else                       // y结点的秩大于等于x结点的秩
    {
        t[x].parent = y;            //将x连到y结点上,y作为x的孩子结点
        if (t[x].rank == t[y].rank) // x和y结点的秩相同
            t[y].rank++;            // y结点的秩增1
    }
}
void sift(Edge E[], int low, int high) //筛选算法
{
    int i = low, j = 2 * i; // E[j]是E[i]的左孩子
    Edge temp = E[i];
    while (j <= high)
    {
        if (j < high && E[j].w < E[j + 1].w) //若右孩子较大,把j指向右孩子
            j++;                             // f变为2i+1
        if (temp.w < E[j].w)
        {
            E[i] = E[j]; //将E[j]调整到双亲结点位置上
            i = j;       //修改i和j值,以便继续向下筛选
            j = 2 * i;
        }
        else
            break; //筛选结束
    }
    E[i] = temp; //被筛选结点的值放入最终位置
}
void HeapSort(Edge E[], int n)
{
    int i;
    Edge temp;
    for (i = n / 2; i >= 1; i--) //循环建立初始堆
        sift(E, i, n);
    for (i = n; i >= 2; i--) //进行n-1次循环,完成推排序
    {
        temp = E[1]; //将第一个元素同当前区间内R[1]对换
        E[1] = E[i];
        E[i] = temp;
        sift(E, 1, i - 1); //筛选R[1]结点,得到i-1个结点的堆
    }
}
void Kruskal(MGraph g, Edge E[])
{
    int i, j, k, u1, v1, sn1, sn2;
    UFSTree t[MaxSize];

    HeapSort(E, g.arcnum); // 采用堆排序对E数组按权值递增排序
    MAKE_SET(t, g.vexnum); // 初始化并查集树t
    k = 1;                 // k表示当前构造生成树的第几条边,初值为1
    j = 1;                 // E中边的下标,初值为1
    while (k < g.vexnum)   // 生成的边数小于n时循环
    {
        u1 = E[j].u;
        v1 = E[j].v; //取一条边的头尾顶点编号u1和v2
        sn1 = FIND_SET(t, u1);
        sn2 = FIND_SET(t, v1); //分别得到两个顶点所属的集合编号
        if (sn1 != sn2)        //两顶点属于不同的集合,该边是最小生成树的一条边
        {
            cout << "  (" << u1 << "," << v1 << "):" << E[j].w << endl;
            k++;              //生成边数增1
            UNION(t, u1, v1); //将u1和v1两个顶点合并
        }
        j++; //扫描下一条边
    }
    cout << endl;
}

//打印图
void print(MGraph g)
{
    int i;
    for (i = 0; i < g.vexnum; i++)
    {
        for (int j = 0; j < g.vexnum; j++)
        {
            if (g.edges[i][j] == INT_MAX)
                cout << "∞"
                     << " ";
            else
                cout << g.edges[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

void createGraph(MGraph &g, Edge E[])
{
    int i, j, k, a;
    // ifstream in("Sparse.txt");
    ifstream in("Dense.txt");
    in >> g.vexnum >> g.arcnum;
    for (i = 0; i < g.vexnum; i++)
        for (j = 0; j < g.vexnum; j++)
        {
            in >> a;
            g.edges[i][j] = a == -1 ? INT_MAX : a;
        }
    k = 1;                             // e数组的下标从1开始计
    for (i = 0; i < g.vexnum - 1; i++) //由g产生的边集e
        for (j = i + 1; j < g.vexnum; j++)
            if (g.edges[i][j] != 0 && g.edges[i][j] != INT_MAX)
            {
                E[k].u = i;
                E[k].v = j;
                E[k].w = g.edges[i][j];
                k++;
            }
}

int main()
{
    int i, j, k;
    MGraph g;
    MyTimer mt;
    Edge E[MaxSize];
    createGraph(g, E);
    print(g);
    cout << "最小生成树为:" << endl;
    mt.Start();
    Kruskal(g, E);
    mt.End();
    cout << "查找最小生成树用时" << mt.costTime << "微秒" << endl;
    system("pause");
    return 0;
}

连通图邻接矩阵生成

#include <iostream>
#include <random>

using namespace std;

int main()
{
    int a[100][100];
    int b, count = 99;
    uniform_int_distribution<unsigned> u(1, 20);
    uniform_int_distribution<unsigned> v(0, 99);
    default_random_engine e;
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            a[i][j] = -1;
        }
    }
    for (int i = 0; i < 100; i++)
    {
        a[i][i] = 0;
    }
    for (int i = 0; i < 99; i++) //确保连通
    {
        a[i][i + 1] = u(e);
        a[i + 1][i] = u(e);
    }
    for (int i = 0; count < 4000; i++) //生成随机边,重复直接覆盖
    {
        int k = v(e);
        int j = v(e);
        if (a[k][j] == -1)
        {
            int r = u(e);
            a[k][j] = r;
            a[j][k] = r;
            count++;
        }
    }
    for (int i = 0; i < 100; i++) //打印邻接矩阵
    {
        for (int j = 0; j < 100; j++)
        {
            cout << a[i][j] << "  ";
        }
        cout << endl;
    }
    cout << "边数:" << count;
    system("pause");
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kruskal算法Prim算法都是用于求解最小生成树的经典算法。下面我将分别介绍这两个算法的基本原理和C++代码实现。 1. Kruskal算法Kruskal算法基于贪心思想,通过不断选择权重最小的边来构建最小生成树。具体步骤如下: - 将图中的所有边按照权重从小到大进行排序。 - 初始化一个空的森林,每个顶点都是一个独立的树。 - 依次选择权重最小的边,如果这条边连接的两个顶点不在同一棵树中,则将这条边加入最小生成树中,并将这两个顶点所在的树合并为一棵树。 - 重复上述步骤,直到最小生成树中包含了图中的所有顶点。 以下是使用C++实现Kruskal算法代码示例: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义边的结构体 struct Edge { int src, dest, weight; }; // 定义并查集的数据结构 struct DisjointSet { int *parent, *rank; int n; DisjointSet(int n) { this->n = n; parent = new int[n]; rank = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; rank[i] = 0; } } int find(int u) { if (u != parent[u]) { parent[u] = find(parent[u]); } return parent[u]; } void merge(int x, int y) { int xroot = find(x); int yroot = find(y); if (rank[xroot] < rank[yroot]) { parent[xroot] = yroot; } else if (rank[xroot] > rank[yroot]) { parent[yroot] = xroot; } else { parent[yroot] = xroot; rank[xroot]++; } } }; // 定义比较函数,用于边的排序 bool compareEdges(Edge a, Edge b) { return a.weight < b.weight; } // Kruskal算法求解最小生成树 void kruskalMST(vector<Edge> edges, int V) { // 对边按照权重进行排序 sort(edges.begin(), edges.end(), compareEdges); // 创建并查集 DisjointSet ds(V); // 存储最小生成树的边 vector<Edge> mst; for (auto edge : edges) { int srcRoot = ds.find(edge.src); int destRoot = ds.find(edge.dest); // 如果这条边的两个顶点不在同一棵树中,则将这条边加入最小生成树中 if (srcRoot != destRoot) { mst.push_back(edge); ds.merge(srcRoot, destRoot); } } // 输出最小生成树的边 for (auto edge : mst) { cout << edge.src << " - " << edge.dest << " : " << edge.weight << endl; } } int main() { int V, E; cout << "Enter the number of vertices: "; cin >> V; cout << "Enter the number of edges: "; cin >> E; vector<Edge> edges(E); cout << "Enter the source, destination and weight of each edge:" << endl; for (int i = 0; i < E; i++) { cin >> edges[i].src >> edges[i].dest >> edges[i].weight; } kruskalMST(edges, V); return 0; } ``` 2. Prim算法Prim算法是一种以顶点为中心的算法,通过不断选择与当前最小生成树相连的权重最小的边来构建最小生成树。具体步骤如下: - 初始化一个空的最小生成树,选择一个起始顶点。 - 将起始顶点加入最小生成树中,并将与起始顶点相连的边加入优先级队列。 - 从优先级队列中选择权重最小的边,如果这条边连接的顶点不在最小生成树中,则将这条边加入最小生成树中,并将与这个顶点相连的边加入优先级队列。 - 重复上述步骤,直到最小生成树中包含了图中的所有顶点。 以下是使用C++实现Prim算法代码示例: ```cpp #include <iostream> #include <vector> #include <queue> using namespace std; // 定义边的结构体 struct Edge { int dest, weight; }; // 定义比较函数,用于边的排序 struct Compare { bool operator()(Edge a, Edge b) { return a.weight > b.weight; } }; // Prim算法求解最小生成树 void primMST(vector<vector<Edge>> graph, int V) { // 存储最小生成树的边 vector<Edge> mst; // 创建一个优先级队列,用于动态排序边 priority_queue<Edge, vector<Edge>, Compare> pq; // 创建一个数组,用于标记顶点是否已经加入最小生成树 vector<bool> visited(V, false); // 选择一个起始顶点 int src = 0; visited[src] = true; // 将起始顶点的所有边加入优先级队列 for (auto edge : graph[src]) { pq.push(edge); } while (!pq.empty()) { // 选择权重最小的边 Edge minEdge = pq.top(); pq.pop(); int dest = minEdge.dest; int weight = minEdge.weight; // 如果这条边连接的顶点不在最小生成树中,则将这条边加入最小生成树中 if (!visited[dest]) { mst.push_back(minEdge); visited[dest] = true; // 将与这个顶点相连的边加入优先级队列 for (auto edge : graph[dest]) { pq.push(edge); } } } // 输出最小生成树的边 for (auto edge : mst) { cout << edge.dest << " - " << edge.weight << endl; } } int main() { int V, E; cout << "Enter the number of vertices: "; cin >> V; cout << "Enter the number of edges: "; cin >> E; vector<vector<Edge>> graph(V); cout << "Enter the source, destination and weight of each edge:" << endl; for (int i = 0; i < E; i++) { int src, dest, weight; cin >> src >> dest >> weight; graph[src].push_back({dest, weight}); graph[dest].push_back({src, weight}); } primMST(graph, V); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值