实验 1 贪心算法实现最小生成树
最小生成树 实验报告

最小生成树实验报告最小生成树实验报告一、引言最小生成树是图论中的一个重要概念,它在实际问题中有着广泛的应用。
本次实验旨在通过编程实现最小生成树算法,并通过实验数据对算法进行分析和评估。
二、算法介绍最小生成树算法的目标是在给定的带权无向图中找到一棵生成树,使得树上所有边的权重之和最小。
本次实验我们选择了两种经典的最小生成树算法:Prim 算法和Kruskal算法。
1. Prim算法Prim算法是一种贪心算法,它从一个顶点开始,逐步扩展生成树的规模,直到包含所有顶点为止。
算法的具体步骤如下:(1)选择一个起始顶点,将其加入生成树中。
(2)从与生成树相邻的顶点中选择一个权重最小的边,将其加入生成树中。
(3)重复上述步骤,直到生成树包含所有顶点。
2. Kruskal算法Kruskal算法是一种基于并查集的贪心算法,它首先将图中的边按权重从小到大进行排序,然后逐个加入生成树中,直到生成树包含所有顶点为止。
算法的具体步骤如下:(1)将图中的边按权重从小到大进行排序。
(2)逐个加入边,如果该边的两个顶点不在同一个连通分量中,则将其加入生成树中。
(3)重复上述步骤,直到生成树包含所有顶点。
三、实验过程本次实验我们使用C++语言实现了Prim算法和Kruskal算法,并通过随机生成的图数据进行了测试。
1. Prim算法的实现我们首先使用邻接矩阵表示图的结构,然后利用优先队列来选择权重最小的边。
具体实现过程如下:(1)创建一个优先队列,用于存储生成树的候选边。
(2)选择一个起始顶点,将其加入生成树中。
(3)将与生成树相邻的顶点及其边加入优先队列。
(4)从优先队列中选择权重最小的边,将其加入生成树中,并更新优先队列。
(5)重复上述步骤,直到生成树包含所有顶点。
2. Kruskal算法的实现我们使用并查集来维护顶点之间的连通关系,通过排序后的边序列来逐个加入生成树中。
具体实现过程如下:(1)将图中的边按权重从小到大进行排序。
Boruvka算法求最小生成树

Boruvka算法求最⼩⽣成树学习了⼀个新的最⼩⽣成树的算法,Boruvka(虽然我不知道怎么读)。
算法思想也是贪⼼,类似于Kruskal。
⼤致是这样的,我们维护图中所有连通块,然后遍历所有的点和边,找到每⼀个连通块和其他连通块相连的最⼩的⼀条边,然后把连通块合并起来,重复这个操作,直到剩下⼀整个连通块,最开始状态是每个点是⼀个单独的连通块。
复杂度是(n+m)longn,因为每次都会合并两个连通块,整个程序进⾏log次操作就会完成,每次操作的复杂度是n+m的。
代码⾮常好理解,我⽤的并查集实现,(然⽽并查集我没有⽤按秩合并,都是细节)。
——by VANE#include<bits/stdc++.h>using namespace std;const int N=5005;const int M=200005;int pre[M<<1],other[M<<1],last[N],l,len[M<<1];int n,m;void add(int x,int y,int z){++l;pre[l]=last[x];last[x]=l;other[l]=y;len[l]=z;}int f[N],mn[2][N];int getfa(int x){return x==f[x]?x:f[x]=getfa(f[x]);}void merge(int x,int y){int fx=getfa(x),fy=getfa(y);f[fx]=fy;}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;++i) f[i]=i;for(int i=1;i<=m;++i){int x,y,z;scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);}int ans=0;while(1){memset(mn[0],127,sizeof mn[0]);bool flag=0;for(int i=1;i<=n;++i){for(int p=last[i];p;p=pre[p]){if(getfa(i)!=getfa(other[p]))if(mn[0][getfa(i)]>len[p]){mn[0][getfa(i)]=len[p];mn[1][getfa(i)]=getfa(other[p]);}}}for(int i=1;i<=n;++i){if(mn[0][i]!=mn[0][0]&&getfa(i)!=getfa(mn[1][i])){flag=1;ans+=mn[0][i];merge(i,mn[1][i]);}}if(!flag) break;}for(int i=1;i<n;++i)if(getfa(i)!=getfa(i+1)){puts("orz");return0;}cout<<ans;}。
贪心算法Kruskal 算法

Kruskal 算法假设给定一个加权连通图G,G的边集合为E,顶点个数为n,要求其一棵最小生成树T。
Kruskal 算法的粗略描述:假设T中的边和顶点均涂成红色,其余边为白色。
开始时G中的边均为白色。
1)将所有顶点涂成红色;2)在白色边中,挑选一条权最小的边,使其与红色边不形成圈,将该白色边涂红;3)重复2)直到有n-1条红色边,这n-1条红色边便构成最小生成树T的边集合。
注意到在算法执行过程中,红色顶点和红色边会形成一个或多个连通分支,它们都是G的子树。
一条边与红色边形成圈当且仅当这条边的两个端点属于同一个子树。
因此判定一条边是否与红色边形成圈,只需判断这条边的两端点是否属于同一个子树。
上述判断可以如此实现:给每个子树一个不同的编号,对每一个顶点引入一个标记t,表示这个顶点所在的子树编号。
当加入一条红色边,就会使该边两端点所在的两个子树连接起来,成为一个子树,从而两个子树中的顶点标记要改变成一样。
综上,可将Kruskal算法细化使其更容易计算机实现。
代码://Kruskal#include "stdio.h"#define maxver 10#define maxright 100int G[maxver][maxver],record=0,touched[maxver][maxver];int circle=0;int FindCircle(int,int,int,int);void main(){int path[maxver][2],used[maxver][maxver];int i,j,k,t,min=maxright,exsit=0;int v1,v2,num,temp,status=0;restart:printf("Please enter the number of vertex(s) in the graph:\n");scanf("%d",&num);if(num>maxver||num<0){printf("Error!Please reinput!\n");goto restart;}for(j=0;j<num;j++)for(k=0;k<num;k++){if(j==k){G[j][k]=maxright;used[j][k]=1;}elseif(j<k){re:printf("Please input the right between vertex %d and vertex %d,if no edge exists please input -1:\n",j+1,k+1);scanf("%d",&temp);if(temp>=maxright||temp<-1){printf("Invalid input!\n");goto re;}if(temp==-1)temp=maxright;G[j][k]=G[k][j]=temp;used[j][k]=used[k][j]=0;touched[j][k]=touched[k][j]=0;}}for(j=0;j<num;j++){path[j][0]=0;path[j][1]=0;}for(j=0;j<num;j++){status=0;for(k=0;k<num;k++)if(G[j][k]<maxright){status=1;break;}if(status==0)break;}for(i=0;i<num-1&&status;i++){for(j=0;j<num;j++)for(k=0;k<num;k++)if(G[j][k]<min&&!used[j][k]){v1=j;v2=k;min=G[j][k];}if(!used[v1][v2])used[v2][v1]=1;touched[v1][v2]=1;touched[v2][v1]=1;path[i][0]=v1;path[i][1]=v2;for(t=0;t<record;t++)FindCircle(path[t][0],path[t][0],num,path[t][0]);if(circle){/*if a circle exsits,roll back*/circle=0;i--;exsit=0;touched[v1][v2]=0;touched[v2][v1]=0;min=maxright;}else{record++;min=maxright;}}}if(!status)printf("We cannot deal with it because the graph is not connected!\n"); else{for(i=0;i<num-1;i++)printf("Path %d:vertex %d to vertex %d\n",i+1,path[i][0]+1,path[i][1]+1); }}int FindCircle(int start,int begin,int times,int pre){ /* to judge whether a circle is produced*/int i;for(i=0;i<times;i++)if(touched[begin][i]==1){if(i==start&&pre!=start){circle=1;return 1;break;}elseif(pre!=i)FindCircle(start,i,times,begin);else continue; }return 1; }。
算法课设实验报告(3篇)

第1篇一、实验背景与目的随着计算机技术的飞速发展,算法在计算机科学中扮演着至关重要的角色。
为了加深对算法设计与分析的理解,提高实际应用能力,本实验课程设计旨在通过实际操作,让学生掌握算法设计与分析的基本方法,学会运用所学知识解决实际问题。
二、实验内容与步骤本次实验共分为三个部分,分别为排序算法、贪心算法和动态规划算法的设计与实现。
1. 排序算法(1)实验目的:熟悉常见的排序算法,理解其原理,比较其优缺点,并实现至少三种排序算法。
(2)实验内容:- 实现冒泡排序、快速排序和归并排序三种算法。
- 对每种算法进行时间复杂度和空间复杂度的分析。
- 编写测试程序,对算法进行性能测试,比较不同算法的优劣。
(3)实验步骤:- 分析冒泡排序、快速排序和归并排序的原理。
- 编写三种排序算法的代码。
- 分析代码的时间复杂度和空间复杂度。
- 编写测试程序,生成随机测试数据,测试三种算法的性能。
- 比较三种算法的运行时间和内存占用。
2. 贪心算法(1)实验目的:理解贪心算法的基本思想,掌握贪心算法的解题步骤,并实现一个贪心算法问题。
(2)实验内容:- 实现一个贪心算法问题,如活动选择问题。
- 分析贪心算法的正确性,并证明其最优性。
(3)实验步骤:- 分析活动选择问题的贪心策略。
- 编写贪心算法的代码。
- 分析贪心算法的正确性,并证明其最优性。
- 编写测试程序,验证贪心算法的正确性。
3. 动态规划算法(1)实验目的:理解动态规划算法的基本思想,掌握动态规划算法的解题步骤,并实现一个动态规划算法问题。
(2)实验内容:- 实现一个动态规划算法问题,如背包问题。
- 分析动态规划算法的正确性,并证明其最优性。
(3)实验步骤:- 分析背包问题的动态规划策略。
- 编写动态规划算法的代码。
- 分析动态规划算法的正确性,并证明其最优性。
- 编写测试程序,验证动态规划算法的正确性。
三、实验结果与分析1. 排序算法实验结果:- 冒泡排序:时间复杂度O(n^2),空间复杂度O(1)。
采用普里姆算法和克鲁斯卡尔算法,求最小生成树 -回复

采用普里姆算法和克鲁斯卡尔算法,求最小生成树-回复普里姆算法和克鲁斯卡尔算法是求解最小生成树问题的两种重要方法。
本文将详细介绍这两种算法的原理和步骤,并比较它们的优缺点和适用场景。
一、普里姆算法普里姆算法(Prim's Algorithm)是一种贪心算法,用于求解带权无向连通图的最小生成树。
它的基本思想是从一个起始顶点开始,逐步向最小代价的边添加顶点,直到生成一颗包含所有顶点的最小生成树。
下面是普里姆算法的具体步骤:1. 随机选择一个顶点作为起始顶点,并将其添加到最小生成树集合中。
2. 从最小生成树集合中已有的顶点出发,寻找与其相连的边中具有最小权值的顶点,将该顶点添加到最小生成树集合中。
3. 重复第二步,直到最小生成树集合包含所有顶点为止。
普里姆算法的时间复杂度为O(V^2),其中V为顶点数。
它的优点是简单易懂、容易实现,并且适用于稠密图。
然而,普里姆算法对于稀疏图的效率较低,因为需要频繁地搜索和更新权值最小的边。
二、克鲁斯卡尔算法克鲁斯卡尔算法(Kruskal's Algorithm)是一种基于边的贪心算法,用于求解带权无向连通图的最小生成树。
它的基本思想是通过选择代价最小的边,并判断是否会形成环路,最终构建出一颗最小生成树。
下面是克鲁斯卡尔算法的具体步骤:1. 将图中的所有边按照权值从小到大进行排序。
2. 依次选择权值最小的边,判断如果添加该边会形成环路,则将其舍弃;否则将其添加到最小生成树的边集合中。
3. 重复第二步,直到最小生成树的边数等于顶点数减一为止。
克鲁斯卡尔算法的时间复杂度为O(ElogE),其中E为边数。
相比普里姆算法,克鲁斯卡尔算法适用于稀疏图,并且对于大规模图的求解效率更高。
然而,克鲁斯卡尔算法的缺点是在构建最小生成树时需要尝试的边较多,因此在边数较多的情况下,算法的效率可能不高。
三、比较与总结普里姆算法和克鲁斯卡尔算法都是求解最小生成树问题的经典算法,它们各自具有不同的优点和适用场景。
克鲁斯卡尔算法求最小生成树完整代码

克鲁斯卡尔算法是一种用来求解最小生成树(Minimum Spanning Tree)的经典算法,它采用了贪心策略,能够高效地找到图中的最小生成树。
下面将为大家介绍克鲁斯卡尔算法的完整代码,希望对大家有所帮助。
1. 算法思路克鲁斯卡尔算法的基本思路是:首先将图中的所有边按照权值进行排序,然后从小到大依次考虑每条边,如果加入该边不会构成环,则将其加入最小生成树中。
在算法执行过程中,我们需要使用并查集来判断是否会构成环。
2. 代码实现接下来,我们将给出克鲁斯卡尔算法的完整代码,代码使用C++语言编写,具体如下:```cpp#include <iostream>#include <vector>#include <algorithm>using namespace std;// 定义图的边struct Edge {int u, v, weight;Edge(int u, int v, int weight) : u(u), v(v), weight(weight) {} };// 定义并查集class UnionFind {private:vector<int> parent;public:UnionFind(int n) {parent.resize(n);for (int i = 0; i < n; i++) {parent[i] = i;}}int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];}void Union(int x, int y) {int root_x = find(x);int root_y = find(y);if (root_x != root_y) {parent[root_x] = root_y;}}};// 定义比较函数用于排序bool cmp(const Edge a, const Edge b) {return a.weight < b.weight;}// 克鲁斯卡尔算法vector<Edge> kruskal(vector<Edge> edges, int n) { // 先对边进行排序sort(edges.begin(), edges.end(), cmp);// 初始化最小生成树的边集vector<Edge> res;UnionFind uf(n);for (int i = 0; i < edges.size(); i++) {int u = edges[i].u, v = edges[i].v, weight = edges[i].weight; // 判断是否构成环if (uf.find(u) != uf.find(v)) {uf.Union(u, v);res.push_back(edges[i]);}}return res;}// 测试函数int m本人n() {vector<Edge> edges;edges.push_back(Edge(0, 1, 4));edges.push_back(Edge(0, 7, 8));edges.push_back(Edge(1, 2, 8));edges.push_back(Edge(1, 7, 11));edges.push_back(Edge(2, 3, 7));edges.push_back(Edge(2, 5, 4));edges.push_back(Edge(2, 8, 2));edges.push_back(Edge(3, 4, 9));edges.push_back(Edge(3, 5, 14));edges.push_back(Edge(4, 5, 10));edges.push_back(Edge(5, 6, 2));edges.push_back(Edge(6, 7, 1));edges.push_back(Edge(6, 8, 6));edges.push_back(Edge(7, 8, 7));vector<Edge> res = kruskal(edges, 9);for (int i = 0; i < res.size(); i++) {cout << res[i].u << " " << res[i].v << " " << res[i].weight << endl;}return 0;}```3. 算法实例上述代码实现了克鲁斯卡尔算法,并对给定的图进行了最小生成树的求解。
算法实验报告贪心

一、实验背景贪心算法是一种在每一步选择中都采取当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的算法策略。
贪心算法并不保证能获得最优解,但往往能获得较好的近似解。
在许多实际应用中,贪心算法因其简单、高效的特点而被广泛应用。
本实验旨在通过编写贪心算法程序,解决经典的最小生成树问题,并分析贪心算法的优缺点。
二、实验目的1. 理解贪心算法的基本原理和应用场景;2. 掌握贪心算法的编程实现方法;3. 分析贪心算法的优缺点,并尝试改进;4. 比较贪心算法与其他算法在解决最小生成树问题上的性能。
三、实验内容1. 最小生成树问题最小生成树问题是指:给定一个加权无向图,找到一棵树,使得这棵树包含所有顶点,且树的总权值最小。
2. 贪心算法求解最小生成树贪心算法求解最小生成树的方法是:从任意一个顶点开始,每次选择与当前已选顶点距离最近的顶点,将其加入生成树中,直到所有顶点都被包含在生成树中。
3. 算法实现(1)数据结构- 图的表示:邻接矩阵- 顶点集合:V- 边集合:E- 已选顶点集合:selected- 最小生成树集合:mst(2)贪心算法实现```def greedy_mst(graph):V = set(graph.keys()) # 顶点集合selected = set() # 已选顶点集合mst = set() # 最小生成树集合for i in V:selected.add(i)mst.add((i, graph[i]))while len(selected) < len(V):min_edge = Nonefor edge in mst:u, v = edgeif v not in selected and (min_edge is None or graph[u][v] < graph[min_edge[0]][min_edge[1]]):min_edge = edgeselected.add(min_edge[1])mst.add(min_edge)return mst```4. 性能分析为了比较贪心算法与其他算法在解决最小生成树问题上的性能,我们可以采用以下两种算法:(1)Prim算法:从任意一个顶点开始,逐步添加边,直到所有顶点都被包含在生成树中。
离散数学大作业——编程实现最小生成树

离散数学大作业——编程实现最小生成树学院:电子工程学院班级:021051学号:*********名:***一、最小生成树概念:设G=(V,E)是无向连通带权图,即一个网络。
E中每条边(v,w)的权为c[v,w]。
所有生成树G’上各边权的总和最小的生成树称为G的最小生成树。
二、prim算法(贪心思想)设图G =(V,E),其生成树的顶点集合为U。
1.把v0放入U。
2.在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。
3.把2找到的边的v加入U集合。
如果U集合已有n个元素,则结束,否则继续执行2其算法的时间复杂度为O(n^2)三、程序源代码# include<stdio.h># include<malloc.h># define m 6# define n 11 typedef struct {int i,tag;char s;}vertice;typedef struct {int a,b,tag;int weight;}edge;vertice v[m];edge e[n];void inititate();void sort();void chuli();int biaoji( edge *s); void print();void main() {inititate();sort();chuli();print();}void inititate() {int i;printf("输入图的%d个顶点:\n",m);for(i=0;i<m;i++) {v[i].i=i+1;v[i].tag=0;scanf("%c",&v[i].s);getchar();}printf("\n输入%d条边的两端顶点及权:\n",n);for(i=0;i<n;i++) {scanf("%d %d %d",&e[i].a,&e[i].b,&e[i].weight);e[i].tag=0;}}int biaoji( edge *s) {int i,j;i=s->a;j=s->b;if(v[i].tag==0 || v[j].tag==0) {v[i].tag=1;v[i].tag=1;s->tag=1;return 1;}return 0;}void print() {int i,j=0;printf("\n最小生成树的边为:\n");for(i=0;i<n&&j<m-1;i++)if(e[i].tag==1) {printf("<%d-%d> ",e[i].a,e[i].b);j++;}printf("\n\n");}void sort() {edge s;int i,j;for(i=0;i<n-1;i++) {for(j=i+1;j<n;j++) {if(e[i].weight>e[j].weight) {s=e[i];e[i]=e[j];e[j]=s;}}}}void chuli() {int i,j=0;edge *s;for(i=0;i<n&&j<m;i++) {s=&e[i];if(biaoji(s)==1)j++;}}四、实验结果输入图的6个顶点:1 2 3 4 5 6输入11条边的权及两端顶点:1 2 11 4 61 6 91 3 112 3 22 4 33 5 83 6 74 5 104 6 45 6 5最小生成树的边为:<1-2> <2-3> <2-4> <4-6> <5-6> Press any key to continue。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验一用贪心算法实现最小生成树问题
一.实验目的
1.熟悉贪心算法的基本原理和使用范围。
二.实验内容及要求
内容:任选一种贪心算法(prim或Kruskal),求解最小生成树。
对算法进行编程。
要求:使用贪心算法编程,求解最小生成树问题
三.程序列表
(1)prim算法
#include<stdio.h>
#define INF 32766
#define max 40
void prim(int g[][max],int n)
{
int lowcost[max],closest[max];
int i,j,k,min;
for(i=2;i<=n;i++)
{
lowcost[i]=g[1][i];
closest[i]=1;
}
lowcost[1]=0;
for(i=2;i<=n;i++)
{
min=INF;
k=0;
for(j=2;j<=n;j++)
{
if((lowcost[j]<min)&&(lowcost[j]!=0))
{
min=lowcost[j];
k=j;
}
}
printf("(%d,%d)%d\t",closest[k],k,min);
lowcost[k]=0;
for(j=2;j<=n;j++)
{
if(g[k][j]<lowcost[j])
{
lowcost[j]=g[k][j];
closest[j]=k;
}
}
printf("\n");
}
}
int adj(int g[][max])
{
int n,i,j,v1,v2,weight,m;
printf("输入顶点数 n=:");
scanf("%d",&n);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
g[i][j]=INF;
while(v1!=0&&v2!=0&&weight!=0)//只要输入0 0 0就结束{
printf("v1,v2,weight=");
scanf("%d %d %d",&v1,&v2,&weight);
g[v1][v2]=weight;
g[v2][v1]=weight;
}
return(n);
}
void prg(int g[][max],int n)
{
int i,j;
for(i=0;i<=n;i++)
printf("%d\t",i);
for(i=1;i<=n;i++)
{
printf("\n%d\t",i);
for(j=1;j<=n;j++)
printf((g[i][j]==INF)?"\t":"%d\t",g[i][j]);
}
printf("\n");
}
void main()
{
int g[max][max],n,i;
n=adj(g);
printf("输出无向图的邻接矩阵:\n");
prg(g,n);
printf("输出最小生成树:\n");
prim(g,n);
}
四.实验结果。