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;
    }
    


  • 相关阅读:
    remove white space from read
    optimize the access speed of django website
    dowload image from requests
    run jupyter from command
    crawl wechat page
    python version 2.7 required which was not found in the registry windows 7
    health
    alternate rows shading using conditional formatting
    word
    【JAVA基础】static 关键字
  • 原文地址:https://www.cnblogs.com/lemonbiscuit/p/7776123.html
Copyright © 2011-2022 走看看