zoukankan      html  css  js  c++  java
  • 数据结构课设--7最小生成树问题

    14、最小生成树问题(**)

    【问题描述】

    若要在n个城市之间建设通信网络,只需要假设n-1条线路即可。如何以最低的经济代价建设这个通信网,是一个网的最小生成树问题。

    【系统要求】

    1.利用克鲁斯卡尔算法求网的最小生成树。

    2.利用普里姆算法求网的最小生成树。

    3.要求输出各条边及它们的权值。

    【测试数据】

    由学生任意指定,但报告上要求写出多批数据测试结果。

    【实现提示】

    通信线路一旦建成,必然是双向的。因此,构造最小生成树的网一定是无向网。设图的顶点数不超过30个,并为简单起见,网中边的权值设成小于100的整数,可利用C语言提供的随机函数产生。

    图的存储结构的选取应和所作操作相适应。为了便于选择权值最小的边,此题的存储结构既不选用邻接矩阵的数组表示法,也不选用邻接表,而是以存储边(带权)的数组表示图。

    【选作内容】

    利用堆排序实现选择权值最小的边。

    一、算法设计

        1.随机生成图我利用的是C语言中的random()和rang()函数,采取系统时间为随机种子,再生成随机数,保证每次生成的图不相同。因为我所构造的图的存储是30*30的邻接矩阵,在构造图的过程中利用随机函数先产生1~30之间的30个随机数。然后从第一个数开始,判断之后的数是否与它相同,如果不同就再利用随机函数生成一个1~90之间的随机数作为边的权值。而第一个数作为边的始点,第二个数作为边的终点,保存进邻接矩阵。当然这不能够保证生成的图是联通的。所以对邻接矩阵进行一次遍历。如果该行所有数全部为0,就产生一个与此时行数不同的1~30之间的数,一个1~90之间的数,重新进行保存,保证生成的图肯定是一个连通图。然后在进行一趟遍历,统计总共有的边数,同时遍历的过程检查是否为无向图,如果不是,行列互换位置赋值,保证是无向图。边集数组构造好之后需要按照权重大小进行排序,这里我利用的是快速排序,与题目的中的堆排序有些出入。边集数组是为克鲁斯卡尔算法做准备的,每次加入一条边检查是否属于构成了环,所以还需要利用一个辅助数组来标记,记为father数组,类似于并查集中的思想。对于普里姆算法,需要将原来的邻接矩阵重新修整,所有为0的地方全部变为INF(即假定的无穷大数值)。在最后计算总花费的时候再利用原来的邻接矩阵计算。

    各个函数之间的调用关系如下图所示:

    main()

    mainjiemian()

    CreatGraph()

    prim()

    quicksort()

    kruskal()

    jieshu()

    partion()

    find()

    mainjiemian()

    2.本程序中包含8个模块

    (1)主函数:int main();

    (2)结束函数:void jieshu();

    (3)主界面函数:void mainjiemian();

    (4)克鲁斯卡尔算法:void kruskal(Edgetype edge[MAX_EDGE_NUM]);

    (5)快速排序函数:

       void quicksort(Edgetype edge[MAX_EDGE_NUM],int low, int high)

       int partion(Edgetype edge[MAX_EDGE_NUM], intlow, int high)

    (6)寻找根结点:int find(intfather[MaxVertexnum], int v);

    (7)普里姆算法:void prim(Graph*G,int w[][100], int fa[], int n);

    (8)随机生成图:voidCreatGraph(Graph *G);

    3.元素类型、结点类型和指针类型

    #define INF 65535

    #define MaxVertexnum 35        /*最大顶点数为30*/

    #define MAX_EDGE_NUM 1000

    typedef int VertexType;          /*顶点类型设置为字符型*/

    typedef int EdgeType;            /*边的权值设为整型*/

    typedef struct

    {

           VertexTypevexs[MaxVertexnum];              /*顶点表*/

           EdgeTypeedges[MaxVertexnum][MaxVertexnum]; /*邻接矩阵,即边表*/

           int n, e;                                    /*顶点数和边数*/

    }Graph; /*Graph是以邻接矩阵存储的图类型*/

    typedef struct

    {

           int v1;

           int v2;

           int cost;

    }Edgetype;

    二、实验测试

        图的生成并不是固定的随机方法,可以保证前后无关联性



    源代码:

    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<stack>
    #include<queue>
    #include<vector>
    #include<string>
    #include<stdlib.h>
    #include<math.h>
    #include<iomanip>
    #include<time.h>
    using namespace std;
    int edgenum = 0;
    #define INF 65535
    #define MaxVertexnum 35         /*最大顶点数为30*/
    #define MAX_EDGE_NUM 1000
    typedef int VertexType;           /*顶点类型设置为字符型*/
    typedef int EdgeType;             /*边的权值设为整型*/
    typedef struct
    {
    	VertexType vexs[MaxVertexnum];                  /*顶点表*/
    	EdgeType edges[MaxVertexnum][MaxVertexnum];     /*邻接矩阵,即边表*/
    	int n, e;                                       /*顶点数和边数*/
    }Graph;                                             /*Graph是以邻接矩阵存储的图类型*/
    typedef struct
    {
    	int v1;
    	int v2;
    	int cost;
    }Edgetype;
    Edgetype edge[MAX_EDGE_NUM];                        /*定义边集数组*/
    void CreatGraph(Graph *G)                           /*建立无向图G的邻接矩阵存储*/
    {
    	int i, j;
    	int ans[30];
    	G->n = 30;
    	printf("随机生成无向图
    ");
    	srand((unsigned)time(NULL));
    	for (i = 0; i < MaxVertexnum; i++)               /*初始化邻接矩阵*/
    	{
    		for (j = 0; j < MaxVertexnum; j++)
    		{
    			G->edges[i][j] = 0;
    		}
    	}
    	for (i = 0; i < 30; i++)
    		ans[i] = rand() % 30 + 1;                     /*产生随机的城市*/
    	for (i = 0; i < 30; i++)                          /*随机生成权值,构造邻接矩阵*/
    	{
    		for (j = i + 1; j < 30; j++)
    		{
    			if (ans[i] != ans[j])
    			{
    				G->edges[ans[i]][ans[j]] = rand() % 90 + 1;
    				G->edges[ans[j]][ans[i]] = G->edges[ans[i]][ans[j]];
    			}
    		}
    	}
    	for (i = 1; i <= G->n; i++)/*完善整个图,使之必定为连通图*/
    	{
    		int flag = 0;
    		for (j = 1; j <= G->n; j++)
    		{
    			if ((G->edges[i][j]!= 0)&&i!=j)
    			{
    				flag = 1;
    				break;
    			}
    		}
    		if (!flag)
    		{
    			int t = i;
    			while (t == i)
    			{
    				t = rand() % 30 + 1;
    			}
    			int v = rand() % 90 + 1;
    			G->edges[i][t] = v; 
    			G->edges[t][i] = v;
    		}
    	}
    	for (i = 1; i <= 30; i++)
    	{
    		for (j = 1; j <=30; j++)
    		{
    			if (G->edges[i][j] != 0)
    				edgenum++;
    		}
    	}
    	i = 1;
    	while (i <= edgenum)
    	{
    		for (j = 1; j <= 30; j++)                       
    		{
    			for (int k = 1; k <= 30; k++)
    			{
    				if (G->edges[j][k] != 0)
    				{
    					edge[i].v1 = j;
    					edge[i].v2 = k;
    					edge[i].cost = G->edges[j][k];      /*边集数组同事也赋值*/
    					i++;
    				}
    			}
    		}
    	}
    	for (i = 1; i <= G->n; i++)
    	{
    		for (j = 1; j <= G->n; j++)
    		{
    			cout << std::left << setw(4) << G->edges[i][j];
    		}
    		printf("
    ");
    	}
    }
    void prim(Graph *G,int w[][100], int fa[], int n)
    {
    	int i, j, m, k;
    	int d[100];                                    /*d[j]可以理解成结点j到生成树(集合)的距离,它的最终值是w[j][fa[j]]*/
    	for (j = 1; j <= n; j++)
    	{
    		d[j] = (j == 1 ? 0 : w[1][j]);             /*将第一个结点加入集合,并初始化集合与其他结点的距离*/
    		fa[j] = 1;                                 /*当前集合中有且只有一个结点1,其他结点暂时未加入集合,所以没有父结点,就先姑且初始化成1*/
    	}
    	for (i = 2; i <= n; i++)
    	{
    		m = INF;
    		for (j = 1; j <= n; j++)
    		if (d[j] <= m && d[j] != 0) m = d[k = j];  /*选取与集合距离最小的边*/
    		d[k] = 0;                                  /*0在这里表示与集合没有距离,也就是说赋值0就是将结点k添加到集合中*/
    		for (j = 1; j <= n; j++)                   /*对刚加入的结点k进行扫描,更新d[j]的值*/
    		if (d[j] > w[k][j] && d[j] != 0)
    		{
    			d[j] = w[k][j];
    			fa[j] = k;
    		}
    	}
    	printf("最小生成树如下:
    ");
    	int cost = 0;
    	for (int i = 2; i <= 30; i++)                  /*打印结果*/  
    	{
    		printf("(%d,%d)
    ", i, fa[i]);
    		cost += G->edges[i][fa[i]];
    	}
    	printf("最少花费成本为:%d
    ", cost);
    }
    int find(int father[MaxVertexnum], int v)/*寻找顶点v所在树的根节点*/
    {
    	while (father[v] >= 0)
    	{
    		v = father[v];
    	}
    	return v;
    }
    int partion(Edgetype edge[MAX_EDGE_NUM], int low, int high)/*对边集数组进行排序*/
    {
    	int i, j;
    	Edgetype t;
    	i = low;
    	j = high;
    	t = edge[low];
    	while (i<j)
    	{
    		while (i<j && edge[j].cost>t.cost)
    			j--;
    		if (i<j)
    			edge[i++] = edge[j];
    		while (i<j && edge[i].cost <= t.cost)
    			i++;
    		if (i<j)
    			edge[j--] = edge[i];
    	}
    	edge[i] = t;
    	return i;
    }
    void quicksort(Edgetype edge[MAX_EDGE_NUM], int low, int high)
    {
    	int i;
    	if (low < high)
    	{
    		i = partion(edge, low, high);
    		quicksort(edge, low, i - 1);
    		quicksort(edge, i + 1, high);
    	}
    }
    void kruskal(Edgetype edge[MAX_EDGE_NUM])
    {
    	int i = 0, vf1, vf2, sum_cost = 0;
    	int father[MaxVertexnum];/*用于辅助判断点顶点是否处于同一连通分量*/
    	/*添加边集数组edges并按照权值由小到大排序*/
    	for (i = 1; i <= 30; i++)
    		father[i] = -1;
    	printf("最小生成树如下:
    ");
    	for (i = 1; i <= edgenum; i++)/*按照权值由小到大的顺序查找符合条件的边*/
    	{
    		vf1 = find(father, edge[i].v1);/*vf1是v1所在树的根节点*/
    		vf2 = find(father, edge[i].v2);/*vf2是v1所在树的根节点*/
    		if (vf1 != vf2)                 /*根节点不同,说明v1和v2不在同一连通分量上*/
    		{
    			father[vf2] = vf1;          /*通过边(v1,v2)连接两个分量*/
    			printf("(%d,%d)
    ", edge[i].v1, edge[i].v2);
    			sum_cost += edge[i].cost;
    		}
    	}
    	printf("最少花费成本为:%d
    ", sum_cost);
    }
    void mainjiemian()
    {
    	cout << "                   ★-----★---------★---------★-----★" << endl;
    	cout << endl;
    	cout << "                   ☆          1   生成随机图          ☆" << endl;
    	cout << "                               2   普里姆算法            " << endl;
    	cout << "                   ☆          3   克鲁斯卡尔算法      ☆" << endl;
    	cout << "                               4   退出                  " << endl;
    	cout << "                   ★-----★---------★---------★-----★" << endl;
    	cout << endl;
    	cout << endl;
    	cout << "请选择数字命令:";
    }
    void jieshu()//结束显示
    {
    	cout << "                   ★-----★---------★---------★-----★" << endl;
    	cout << endl;
    	cout << "                   ☆           感谢您的使用!         ☆" << endl;
    	cout << endl;
    	cout << "                   ★-----★---------★---------★-----★" << endl;
    	cout << endl;
    }
    int main()
    {
    	system("color 57");
    	char*end;/*末端指针*/
    	string order;
    	mainjiemian();
    	Graph *G = new Graph;
    	while (cin >> order)
    	{
    		int a_order = static_cast<int>(strtol(order.c_str(), &end, 10));/*将输入进来的值转化为int类型*/
    		switch (a_order + 48)
    		{
    		case'1':
    		{
    				   system("cls");
    				   CreatGraph(G);
    				   system("pause");
    				   system("cls");
    				   mainjiemian();
    				   break;
    		}
    		case'2':
    		{
    				   system("cls");
    				   int fa[MaxVertexnum];
    				   int w[100][100];
    				   for (int i = 1; i <= G->n; i++)
    				   {
    					   for (int j = 1; j <= G->n; j++)
    					   {
    						   w[i][j] = (G->edges[i][j] == 0) ? INF : G->edges[i][j];
    					   }
    				   }
    				   prim(G, w, fa, G->n);
    				   system("pause");
    				   system("cls");
    				   mainjiemian();
    				   break;
    		}
    		case'3':
    		{
    				   system("cls");
    				   quicksort(edge, 1, edgenum);/*边集数组按照权重由小到大排序*/
    				   kruskal(edge);
    				   system("pause");
    				   system("cls");
    				   mainjiemian();
    				   break;
    		}
    		case'4':
    		{
    				   system("cls");
    				   jieshu();
    				   return 0;
    				   break;
    		}
    		default:
    		{
    				   cin.clear();
    				   cin.sync();
    				   cout << "输入错误,重新返回主界面。" << endl;
    				   system("pause");
    				   system("cls");
    				   mainjiemian();
    				   break;
    		}
    		}
    	}
    	return 0;
    }
    


  • 相关阅读:
    windows 下的 Apache SSL证书配置
    javascript xml字符串转为json对象
    php 服务端允许跨域访问
    前端自动化构建工具 gulp 学习笔记 一、
    mysql使用select语句导出表数据时,报error 1290解决方法
    批量操作系统服务的脚本(windows关闭服务脚本)
    Dism++备份还原系统
    Win10开启蓝屏信息记录及文件查看位置的方法
    MySql 8.0服务端安装后,用navicat12连接时报2059错误_解决
    软件测试之细节功能测试_注意点
  • 原文地址:https://www.cnblogs.com/lemonbiscuit/p/7776123.html
Copyright © 2011-2022 走看看