c/c++常用算法(15) -- 经典数据结构(城市之间的最短距离问题)

一、最短总距离算法:


1.描述


        我们先来分析一下这个问题。某个地区n个城市构成一个交通图,我们可以使用图结构来描述这个问题,其对应关系如下:

  • 每个城市代表一个图中的一个顶点。
  • 两个顶点之间的边就是两个城市之间的路径,边的权值代表了城市间的距离。

        这样,求解各个城市之间的最短总距离问题就归结为该图的最小生成树问题。


2.最小生成树


        一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。如图:





3.基本思想


        假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:
           在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
           此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。


4.实现代码(最短总距离算法)

#include <iostream>
#include <time.h>
#include <string.h>

#define MaxNum 20           //图的最大顶点数
#define MaxValue 65535      //最大值

#define USED 0;             //已选用顶点
#define NoL -1              //非邻接顶点

typedef struct
{
	char Vertex[MaxNum];            //保存顶点信息(序号或字母)
	int GType;                      //图的类型(0:无向图,1)
	int VertexNum;                  //顶点的数量
	int EdgeNum;                    //边的数量
	int EdgeWeight[MaxNum][MaxNum]; //保存边的权
	int isTrav[MaxNum];             //定义邻接矩阵图结构
}GraphMatrix;

void CreateGraph(GraphMatrix *GM)
{
	int i,j,k;
	int weight;
	char EstartV,EendV;
    
	std::cout<<"输入图中各顶点信息\n";
	for (i = 0; i<GM->VertexNum; i++)
	{
		getchar();
		printf("第%d个顶点:",i+1);
		scanf("%c",&(GM->Vertex[i]));
	}
	std::cout<<"输入构成各边的顶点及权值:\n";
	for (k = 0; k <GM->EdgeNum; k++)
	{
		getchar();
		printf("第%d条边:",k+1);
		scanf("%c %c %d",&EstartV,&EendV,&weight);
		for ( i= 0; EstartV != GM->Vertex[i]; i++);
		for ( j= 0; EendV != GM->Vertex[j]; j++);
		GM->EdgeWeight[i][j] = weight;
		if (GM->GType == 0) //若是无向图
		{
			GM->EdgeWeight[j][i] = weight;
		}
	}
}

void ClearGraph(GraphMatrix *GM)
{
	int i,j;
	for (i=0; i<GM->VertexNum; i++)
	{
		for (j=0; j<GM->VertexNum; j++)
		{
			GM->EdgeWeight[i][j] = MaxValue;
		}
	}
}

void OutGraph(GraphMatrix *GM)
{
	int i,j;
	for (j=0; j<GM->VertexNum; j++)
	{
		printf("\t%c",GM->Vertex[j]);
	}
	std::cout<<"\n";
    
	for (i=0; i<GM->VertexNum; i++)
	{
		printf("%c",GM->Vertex[i]);
		for (j=0; j<GM->VertexNum; j++)
		{
			if (GM->EdgeWeight[i][j] == MaxValue)
			{
				printf("\tZ");  //以Z表示无穷大
			}
			else
			{
				printf("\t%d",GM->EdgeWeight[i][j]);  //输出边的权值
			}
		}
		std::cout<<"\n";
	}
}

void PrimGraph(GraphMatrix GM)          //最小生成树算法
{
	int i,j,k,min,sum;
	int weight[MaxNum];                 //权值
	char vtempx[MaxNum];                //临时顶点信息
    
	sum = 0;
	for(i = 1; i<GM.VertexNum;i++)      //保存邻接矩阵中的一行数据
	{
		weight[i] = GM.EdgeWeight[0][i];
		if (weight[i] == MaxValue)
		{
			vtempx[i] = NoL;
		}
		else
		{
			vtempx[i] = GM.Vertex[0];               //邻接顶点
		}
	}
	vtempx[0] = USED;                               //选用
	weight[0] = MaxValue;
	for (i = 1; i<GM.VertexNum; i++)
	{
		min = weight[0];                            //最小权值
		k = i;
		for (j = 1; j<GM.VertexNum; j++)
		{
			if (weight[j] < min && vtempx[j]>0)     //到具有更小权值的未使用边
			{
				min = weight[j];                    //保存权植
				k = j;                              //保存邻接点序号
			}
		}
		sum +=min;                                  //权值累加
		printf("(%c,%c),",vtempx[k],GM.Vertex[k]);  //输出生成树一条边
		vtempx[k] = USED;
		weight[k] = MaxValue;
		for (j = 0; j<GM.VertexNum; j++)            //重新选择最小边
		{
			if (GM.EdgeWeight[k][j] < weight[j] && vtempx[j]!= 0)
			{
				weight[j] = GM.EdgeWeight[k][j];    //权植
				vtempx[j] = GM.Vertex[k];
			}
		}
	}
	printf("\n最小生成树的总权值为:%d\n",sum);
}


int main(int argc, const char * argv[])
{
	// std::cout << "Hello, World!\n";
	GraphMatrix GM;
	char again;
    
	printf("寻找最小生成树!\n");
    
	printf("请先输入生成图的类型:");
	scanf("%d",&GM.GType);
	printf("输入图的顶点数量:");
	scanf("%d",&GM.VertexNum);
    
	printf("输入图的边数量:");
	scanf("%d",&GM.EdgeNum);
    
	ClearGraph(&GM);
	CreateGraph(&GM);
	printf("该图的邻接矩阵数据如下:\n");
	OutGraph(&GM);
    
	printf("最小生成树的边为:");
	PrimGraph(GM);
    
	printf("\n继续玩(y/n)");
	fflush(stdin);
	scanf("%c",&again);
    
    
	printf("\n");
    
    
	return 0;
}

演示结果:


                


演示效果图:


                


二、最短路径


1.描述


        求解城市之间的最短距离是一个非常实际的问题。其大意如下:

        某个地区有n个城市,如何选择路线使某个城市到某个指定城市的距离最短?


        注:这里需要求解的最短路径指两个城市之间的最短距离,面不是所有城市之间最短总距离。


2.基本思想


算法步骤如下:


        1. 初始时令 S={V0},T={其余顶点},T中顶点对应的距离值
                若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值
                若不存在<V0,Vi>,d(V0,Vi)为∞
        2. 从T中选取一个其距离值为最小的顶点W且不在S中,加入S
        3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值
重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止


3.实现代码

                

#include <iostream>
#include <time.h>
#include <string.h>

#define MaxNum 20           //图的最大顶点数
#define MaxValue 65535      //最大值
int  path[MaxNum];          //两点经过的顶点集合的数组
int tmpvertex[MaxNum];      //最短路径的起始点集合

#define USED 0;             //已选用顶点
#define NoL -1              //非邻接顶点

typedef struct
{
	char Vertex[MaxNum];            //保存顶点信息(序号或字母)
	int GType;                      //图的类型(0:无向图,1)
	int VertexNum;                  //顶点的数量
	int EdgeNum;                    //边的数量
	int EdgeWeight[MaxNum][MaxNum]; //保存边的权
	int isTrav[MaxNum];             //定义邻接矩阵图结构
}GraphMatrix;

void CreateGraph(GraphMatrix *GM)
{
	int i,j,k;
	int weight;
	char EstartV,EendV;
    
	std::cout<<"输入图中各顶点信息\n";
	for (i = 0; i<GM->VertexNum; i++)
	{
		getchar();
		printf("第%d个顶点:",i+1);
		scanf("%c",&(GM->Vertex[i]));
	}
	std::cout<<"输入构成各边的顶点及权值:\n";
	for (k = 0; k <GM->EdgeNum; k++)
	{
		getchar();
		printf("第%d条边:",k+1);
		scanf("%c %c %d",&EstartV,&EendV,&weight);
		for ( i= 0; EstartV != GM->Vertex[i]; i++);
		for ( j= 0; EendV != GM->Vertex[j]; j++);
		GM->EdgeWeight[i][j] = weight;
		if (GM->GType == 0) //若是无向图
		{
			GM->EdgeWeight[j][i] = weight;
		}
	}
}

void ClearGraph(GraphMatrix *GM)
{
	int i,j;
	for (i=0; i<GM->VertexNum; i++)
	{
		for (j=0; j<GM->VertexNum; j++)
		{
			GM->EdgeWeight[i][j] = MaxValue;
		}
	}
}

void OutGraph(GraphMatrix *GM)
{
	int i,j;
	for (j=0; j<GM->VertexNum; j++)
	{
		printf("\t%c",GM->Vertex[j]);
	}
	std::cout<<"\n";
    
	for (i=0; i<GM->VertexNum; i++)
	{
		printf("%c",GM->Vertex[i]);
		for (j=0; j<GM->VertexNum; j++)
		{
			if (GM->EdgeWeight[i][j] == MaxValue)
			{
				printf("\tZ");  //以Z表示无穷大
			}
			else
			{
				printf("\t%d",GM->EdgeWeight[i][j]);  //输出边的权值
			}
		}
		std::cout<<"\n";
	}
}

void DisMin(GraphMatrix GM,int vend)    //最短路径算法
{
    int weight[MaxNum];                 //某终止点到各顶点的最短路径长度
    int i,j,k,min;
    vend--;
    
    for (i =0; i<GM.VertexNum; i++)             //初始weight数组
    {
        weight[i] = GM.EdgeWeight[vend][i];     //保存最小权值
    }
    
    for (i=0; i<GM.VertexNum; i++)              //初始path数组
    {
        if (weight[i]<MaxValue && weight[i]>0)  //有效权值
        {
            path[i] = vend;                     //保存边
        }
    }
    
    for (i=0; i<GM.VertexNum; i++)              //初始tmpvertex数组
    {
        tmpvertex[i] = 0;                       //初始化顶点集合为空
    }
    
    tmpvertex[vend] = 1;                        //选入顶点vend
    weight[vend] = 0;
    for (i = 0; i<GM.VertexNum; i++)
    {
        min = MaxValue;
        k = vend;
        for (j=0; j < GM.VertexNum; j++)
        {
            if (tmpvertex[j] == 0 && weight[j] <min)    //查找未用顶点的最小权值
            {
                min = weight[j];
                k = j;
            }
        }
        
        tmpvertex[k] = 1;                               //将顶点k选入
        for (j = 0; j<GM.VertexNum; j++)
        {
            if (tmpvertex[j] == 0 && weight[k] + GM.EdgeWeight[k][j] < weight[j])
            {
                weight[j] = weight[k] + GM.EdgeWeight[k][j];
                path[j] = k;
            }
        }
    }
}



int main(int argc, const char * argv[])
{
	// std::cout << "Hello, World!\n";
	GraphMatrix GM;
	char again;
    int vend;
    int i,k;
    
	printf("求解最短路径问题!\n");
    
	printf("请先输入生成图的类型:");
	scanf("%d",&GM.GType);
	printf("输入图的顶点数量:");
	scanf("%d",&GM.VertexNum);
    
	printf("输入图的边数量:");
	scanf("%d",&GM.EdgeNum);
    
	ClearGraph(&GM);
	CreateGraph(&GM);
	
    printf("\n请输入结束点:");
    scanf("%d",&vend);
    
    DisMin(GM, vend);
    
    vend--;
    printf("\n各顶点到达顶点%c的最短路径分别为(起始点-结束点):\n",GM.Vertex[vend]);
    for (i = 0; i <GM.VertexNum; i++)
    {
        if (tmpvertex[i] == 1)
        {
            k = i;
            while (k != vend)
            {
                printf("顶点%c-",GM.Vertex[k]);
                k = path[k];
            }
            printf("顶点%c\n",GM.Vertex[k]);
        }
        else
        {
            printf("%c - %c:无路径\n",GM.Vertex[i],GM.Vertex[vend]);
        }
    }
    
	printf("\n");
    
    
	return 0;
}
演示结果:

                


演示效果图:


                


参考书籍:《C/C++常用算法手册》

  • 18
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: C/C++算法常用手册是程序员日常工作中不可或缺的工具书之一。该手册主要收录了程序员在开发过程中常用算法,以及相应的代码实现。该手册涵盖了诸如数据结构、排序、查找、递归、贪心、动态规划、字符串等算法,帮助程序员快速掌握这些算法的基本原理和实现方式。简单地说,该手册将算法的核心原理和实现细节集中在了一起,兼顾了易懂性和实用性。 随着程序员需求的不断增加,该手册逐渐扩充了更多的算法类型。同时,该手册还根据算法的不同应用场景进行分类,方便程序员快速查找和使用。例如,程序员可以通过该手册快速了解不同数据结构的原理和实现方法,了解常见算法的时间复杂度和空间复杂度,还可以查找常见的实际问题中的算法实现方式。 总的来说,C/C++算法常用手册是程序员必备的工具之一,帮助程序员提高算法的实现能力和解决实际问题的能力,提高程序的效率和质量。 ### 回答2: C/C++常用算法手册是一本介绍计算机算法的参考手册,主要面向C/C++语言程序员。该手册总结了各种常用算法,包括排序、查找、图论、字符串等。通过该手册的学习,可以让程序员更好地掌握C/C++编程的技巧和方法。 该手册中介绍了排序算法,包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。对于不同的排序算法,手册详细介绍了它们的思路和实现方法,同时也对它们的时间复杂度和效率进行了分析和比较。 在查找方面,手册介绍了常用的顺序查找和二分查找算法,它们可以帮助程序员快速地定位和查找数据。 在图论和字符串方面,手册介绍了很多有用的算法,如最短路径算法、最小生成树算法、字符串匹配算法等。这些算法可以帮助程序员更好地解决实际问题。 总之,C/C++常用算法手册是一本非常实用和有价值的参考书,它可以让程序员掌握更多的C/C++算法技巧,提高程序员的编程能力和开发效率。 ### 回答3: C/C++ 常用算法手册是一本总结了 C/C++ 编程语言中常用算法数据结构、设计模式等知识的参考书籍。 相对于其他语言,C 和 C++ 语言有着更高的执行效率和更多的编程自由度,也因此被广泛应用于开发高性能、底层的软件程序。在这样的应用场景下,对算法数据结构的掌握显得尤为重要。 C/C++ 常用算法手册涵盖了各种基础的算法数据结构,比如排序、查找、链表、树等。同时,它也介绍了一些常用的高级算法,比如动态规划、贪心算法和回溯算法。 此外,该手册还详细说明了面向对象编程领域中常用的设计模式和其实现方式,例如工厂模式、装饰器模式等。 阅读 C/C++ 常用算法手册不但能够让读者掌握常用算法的实现方法,更能提高编程思维和技巧。另外,在实际应用中,编写高效的程序不仅需要算法的巧妙运用,更需要细致、严谨的代码风格和设计思路。此时,该手册中丰富的示例代码和编码规范性的讲解也能为读者提供很大的帮助。 总之,C/C++ 常用算法手册是一本既实用又深入的参考书,适合广大 C/C++ 开发者和算法学习者阅读。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热血枫叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值