第十三周项目1Prim算法的验证和Kruskal算法的验证

本文介绍使用Prim和Kruskal算法求解最小生成树的过程,并提供了完整的C语言实现代码。通过邻接矩阵表示带权图,最终输出最小生成树的每条边及其权重。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题代码:

/*问题及代码
 *Copyright(c)2016,烟台大学计算机学院
 *All right reserved.
 *文件名 ll.cpp
 *作者:李玲
 *时间:11月24日
 *版本号;v1.0
 *问题描述:
 文件名称:Prim算法的验证和Kruskal算法的验证.cpp

 *输入描述:带权图的邻接矩阵
 *程序输出:最小生成树各边以及权值。
*/

#include <stdio.h>
#include <malloc.h>
#define MAXV 100                //最大顶点个数
#define INF 32767       //INF表示∞
#define MaxSize 100
typedef int InfoType;

//以下定义邻接矩阵类型
typedef struct
{
    int no;                     //顶点编号
    InfoType info;              //顶点其他信息,在此存放带权图权值
} VertexType;                   //顶点类型

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

//以下定义邻接表类型
typedef struct ANode            //弧的结点结构类型
{
    int adjvex;                 //该弧的终点位置
    struct ANode *nextarc;      //指向下一条弧的指针
    InfoType info;              //该弧的相关信息,这里用于存放权值
} ArcNode;

typedef int Vertex;

typedef struct Vnode            //邻接表头结点的类型
{
    Vertex data;                //顶点信息
    int count;                  //存放顶点入度,只在拓扑排序中用
    ArcNode *firstarc;          //指向第一条弧
} VNode;

typedef VNode AdjList[MAXV];    //AdjList是邻接表类型

typedef struct
{
    AdjList adjlist;            //邻接表
    int n,e;                    //图中顶点数n和边数e
} ALGraph;                      //图的邻接表类型
typedef struct
{
    int u;     //边的起始顶点
    int v;     //边的终止顶点
    int w;     //边的权值
} Edge;
void ArrayToMat(int *Arr, int n, MGraph &g);
void Prim(MGraph g,int v);
void InsertSort(Edge E[],int n);
void Kruskal(MGraph g);
void ArrayToMat(int *Arr, int n, MGraph &g)
{
    int i,j,count=0;  //count用于统计边数,即矩阵中非0元素个数
    g.n=n;
    for (i=0; i<g.n; i++)
        for (j=0; j<g.n; j++)
        {
            g.edges[i][j]=Arr[i*n+j]; //将Arr看作n×n的二维数组,Arr[i*n+j]即是Arr[i][j],计算存储位置的功夫在此应用
            if(g.edges[i][j]!=0)
                count++;
        }
    g.e=count;
}



void InsertSort(Edge E[],int n) //对E[0..n-1]按递增有序进行直接插入排序
{
    int i,j;
    Edge temp;
    for (i=1; i<n; i++)
    {
        temp=E[i];
        j=i-1;              //从右向左在有序区E[0..i-1]中找E[i]的插入位置
        while (j>=0 && temp.w<E[j].w)
        {
            E[j+1]=E[j];    //将关键字大于E[i].w的记录后移
            j--;
        }
        E[j+1]=temp;        //在j+1处插入E[i]
    }
}

void Kruskal(MGraph g)
{
    int i,j,u1,v1,sn1,sn2,k;
    int vset[MAXV];
    Edge E[MaxSize];    //存放所有边
    k=0;                //E数组的下标从0开始计
    for (i=0; i<g.n; i++)   //由g产生的边集E
        for (j=0; j<g.n; j++)
            if (g.edges[i][j]!=0 && g.edges[i][j]!=INF)
            {
                E[k].u=i;
                E[k].v=j;
                E[k].w=g.edges[i][j];
                k++;
            }
    InsertSort(E,g.e);      //采用直接插入排序对E数组按权值递增排序
    for (i=0; i<g.n; i++)   //初始化辅助数组
        vset[i]=i;
    k=1;    //k表示当前构造生成树的第几条边,初值为1
    j=0;    //E中边的下标,初值为0
    while (k<g.n)       //生成的边数小于n时循环
    {
        u1=E[j].u;
        v1=E[j].v;      //取一条边的头尾顶点
        sn1=vset[u1];
        sn2=vset[v1];   //分别得到两个顶点所属的集合编号
        if (sn1!=sn2)   //两顶点属于不同的集合
        {
            printf("  (%d,%d):%d\n",u1,v1,E[j].w);
            k++;                     //生成边数增1
            for (i=0; i<g.n; i++)   //两个集合统一编号
                if (vset[i]==sn2)   //集合编号为sn2的改为sn1
                    vset[i]=sn1;
        }
        j++;               //扫描下一条边
    }
}

int main()
{
    MGraph g;
    int A[6][6]=
    {
        {0,6,1,5,INF,INF},
        {6,0,5,INF,3,INF},
        {1,5,0,5,6,4},
        {5,INF,5,0,INF,2},
        {INF,3,6,INF,0,6},
        {INF,INF,4,2,6,0}
    };
    ArrayToMat(A[0], 6, g);
    printf("最小生成树构成:\n");
    Kruskal(g);
    return 0;
}


运行结果:

知识点总结:

普里姆算法中有函数是寻找某一顶点延伸出边的最小权值的,有一条是时刻记录路径的,一定要注意closest函数的用法。

学习心得:

此程序相对还是比较容易的。画图很重要

学习心得:

### Prim算法Kruskal算法的比较 #### 工作原理的不同 Prim算法是从任意一个顶点开始构建最小生成树,逐步扩展已有的部分直到覆盖所有顶点。每次迭代过程中选取当前未被纳入生成树集合中的离已有结构最近的一个节点并将其加入其中[^4]。 相比之下,Kruskal算法则侧重于处理边而非顶点。该方法首先按照权值升序排列所有的边,在遍历这些有序列表的同时尝试添加每条边至正在形成的森林里——只要这样做不会形成环路即可[^5]。 #### 数据结构的选择 对于Prim算法而言,通常会利用优先队列来高效找到下一个要访问的最佳候选者;而在实现上可以借助二叉堆或是斐波那契堆等高级数据结构进一步优化性能表现[^3]。 另一方面,由于Kruskal算法涉及频繁查询两个端点是否属于同一连通分量的操作,因此一般配合不相交集(Union-Find)的数据结构使用以加速此类判断过程[^1]。 #### 时间复杂度分析 当面对稠密图时,即边的数量接近于\(V^2\)的情况之下,Prim算法能够展现出更优的时间效率,特别是通过邻接矩阵表示法下的朴素版本更是如此。然而,在稀疏图的情况下,则应考虑采用基于边表表达形式的Kruskal方案,其主要开销在于初始阶段对全部边实施排序操作\[O(E\log E)\]。 #### 应用场景建议 针对具体的应用环境选择合适的最小生成树算法至关重要: - 对于较为密集的网络拓扑结构,推荐选用Prim算法; - 如果待解决问题所对应的图形呈现明显稀疏特征,则更适合应用Kruskal算法来进行求解。 ```cpp // 示例代码展示两种算法的核心逻辑差异 (C++) // Prim Algorithm Core Logic void prim(vector<vector<int>>& graph, int start){ priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq; vector<bool> visited(graph.size(), false); pq.push({0,start}); while(!pq.empty()){ auto [weight,node]=pq.top(); pq.pop(); if(visited[node]) continue; visited[node]=true; // Process node and add adjacent nodes to the queue... } } // Kruskal Algorithm Core Logic struct Edge { int u,v,w; }; bool cmp(const Edge& a,const Edge& b){return a.w<b.w;} int find(int x,vector<int>& parent); void kruskal(vector<Edge>& edges, int n){ sort(edges.begin(),edges.end(),cmp); vector<int>parent(n),rank(n,0); iota(parent.begin(),parent.end(),0); for(auto &e : edges){ int pu=find(e.u,parent),pv=find(e.v,parent); if(pu!=pv){ union_sets(pu,pv,parent,rank); // Add edge e into MST ... } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值