PTA 旅游规划(邻接表) 思路分析及代码解析

一、前导

1. 需要掌握的知识

  1. 图的最短路径、Dijkstra最短路算法

2. 题目信息

  1. 题目来源:PTA / 拼题A
  2. 题目地址:旅游规划

二、解题思路分析

1. 题意理解

  1. 单源有权图最短路问题,在本题中有两个最短路维度:距离维度和价格维度
  2. 已经使用过邻接矩阵的方式解决,详情请参考 PTA 旅游规划(邻接矩阵) 思路分析及代码解析,本文使用邻接表,重点介绍与前文不同的部分

1. 1 输入数据

4 5 0 3 //城市数 4、高速公路数 5、起点以及终点 0 3
//如下5行是具体的高速公路信息:起点 终点 距离 价位
0 1 1 20 
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

1.2 输出数据

  1. 输出起点到终点的最短路径及花费的金额。需要注意,当存在多条最短路径时,输出价位最低的那条路径
3 40 

2. 思路分析(重点)

  1. 这道题是一个单源无权图最短路径问题,与哈利·波特的考试属于同类题,同样使用Dijkstra算法解决
  2. 本题中最短路包含两个维度:两地之间的最短距离 和 两地之间的最低收费,其中最短距离优先级更高
  3. 从起点执行Dijkstra算法,从最短距离、最低收费两个维度进行代码的实现

三、具体实现

1. 弯路和bug

  1. 图的最短路问题,多练习才能熟能生巧

2. 代码框架(重点)

2.1 采用的数据结构

  1. 邻接表:依次构建边、邻接点、邻接表中的头结点、图的数据结构,图中包含头结点数组、头结点数组存储其邻接点
typedef int vertex;
typedef int weight;

struct EdgeStructure //边
{
	vertex V1;
	vertex V2;
	weight HighwayDist;
	weight HighwayCost;
};
typedef struct EdgeStructure *PtrEdge;

typedef struct AdjNodeStructure *PtrAdjNode; //邻接点
struct AdjNodeStructure
{
	vertex vertexNumber;
	weight HighwayDist;
	weight HighwayCost;	
	PtrAdjNode Next;
};

struct HeadNode  //头结点
{
	PtrAdjNode AdjNode;
};
typedef struct HeadNode HeadNodeArray[max]; //头结点数组

struct GraphStructure //图
{
	int VertexNumber;
	int EdgeNumber;
	HeadNodeArray headNode;
};
typedef struct GraphStructure *ptrGraph;
  1. 对于本题,需要三个数组配合Dijkstra( )算法
weight dist[max]; //各顶点到源点的距离和费用
weight cost[max];
bool visited[max]; //用于标记顶点是否收录到虚拟集合S中

2.2 程序主体框架

               程序伪码描述
int main()
{	
	1.构建图,通过邻接表存储图的顶点和边
	2.执行Dijkstra( )算法
	3.打印结果
	return 0;
}

2.3 各分支函数

  1. buildGraph( ):根据输入数据,完成图的构建。createNoEdgeGraph( )子函数用于构建无边图,insertEdge( )子函数用于向图中插入边。创建该函数属于基本功练习
vertex source=maxValue,destination=maxValue;
ptrGraph Graph; //方便起见,使用了全局变量,此处不严谨
void buildGraph()
{
	int VertexNumber;
	PtrEdge Edge; 
	
	cin>>VertexNumber;
	createNoEdgeGraph(VertexNumber);
	cin>>Graph->EdgeNumber;
	cin>>source>>destination;
	
	for(int i=0;i<Graph->EdgeNumber;i++)
	{
		Edge=(PtrEdge)malloc(sizeof(struct EdgeStructure));
		cin>>Edge->V1>>Edge->V2;
		cin>>Edge->HighwayDist>>Edge->HighwayCost;
		insertEdge(Edge);
	}
	return;
}

void insertEdge(PtrEdge Edge)
{	
	PtrAdjNode Node;
	Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
	Node->Next=Graph->headNode[Edge->V1].AdjNode;
	Node->vertexNumber=Edge->V2;
	Node->HighwayDist=Edge->HighwayDist;
	Node->HighwayCost=Edge->HighwayCost;
	Graph->headNode[Edge->V1].AdjNode=Node;
	
	Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure)); //无向图 需要插入两次
	Node->Next=Graph->headNode[Edge->V2].AdjNode;
	Node->vertexNumber=Edge->V1;
	Node->HighwayDist=Edge->HighwayDist;
	Node->HighwayCost=Edge->HighwayCost;
	Graph->headNode[Edge->V2].AdjNode=Node;
	
	return;
}

void createNoEdgeGraph(int vertexNumber)
{
	Graph=(ptrGraph)malloc(sizeof(struct GraphStructure));
	Graph->VertexNumber=vertexNumber;
	Graph->EdgeNumber=0;
	
	for(int i=0;i<Graph->VertexNumber;i++)
		Graph->headNode[i].AdjNode=NULL; //空图无边,因此邻接点赋值为NULL
		
	return ;
}

  1. Dijkstra算法复习:最短路径不是一次成型的,而是逐步成型的,具体步骤包括:
    (1) 令S为一个集合(对应上面的visited数组),集合S包括源点source 以及 已经确定了最短路径的顶点v。集合S一开始为空(对应visited数组的元素初值都是false),第一个收录进集合S的是源点
    (2) 对任意一个未收录的顶点v,定义 dist[v] 为:源点s到顶点v的最短路径,但该路径仅经过集合S中的顶点,即路径 { s–(vi属于S)–v }得到的最小长度
    (3) 除源点外,每次从未收录的顶点中选一个dist最小的收录,按递增(非递减)的顺序生成集合S
    (4) 增加一个顶点v进入集合S时,可能影响v的邻接点 i 的dist值(顶点i未在集合S中),需要更新dist为较小值。重复(3),直到符合要求的顶点都收入到集合S中
  2. 按Dijkstra算法的思路实现即可。使用邻接表存储图时,邻接点的获取还是蛮方便的,效率也高 :)。另外需要注意,当dist相同时,比较cost
void Dijkstra(vertex source)
{  
	PtrAdjNode adjNode=NULL;
	vertex vertexDistMin=Null;
	weight distMin=maxValue;
	
	visited[source]=true; //将源点收入集合S中
	adjNode=Graph->headNode[source].AdjNode;
	
	while(adjNode) //收入后更新源点S的邻接点dist(距离源点的距离) 和 cost(距离源点的路径)
	{
		if( !visited[adjNode->vertexNumber])
		{
			dist[adjNode->vertexNumber]=adjNode->HighwayDist;
			cost[adjNode->vertexNumber]=adjNode->HighwayCost;
		}
		adjNode=adjNode->Next;		
	}
	
	while(true) 
	{
		for(int i=0;i<Graph->VertexNumber;i++) //找到未收录的dist最小的顶点
		{
			if(distMin>dist[i] && !visited[i])
			{
				distMin=dist[i];
				vertexDistMin=i;
			}
		}	
		
		if(vertexDistMin==Null) break;	
		
		visited[vertexDistMin]=true; //收录该顶点
		
		adjNode=Graph->headNode[vertexDistMin].AdjNode;
		while(adjNode) //收入后更新该顶点的 且 未收录到集合S的邻接点dist(距离源点的距离) 和 cost(距离源点的路径)
		{
			if(!visited[adjNode->vertexNumber])
			{
				if(dist[adjNode->vertexNumber] > (dist[vertexDistMin] + adjNode->HighwayDist))
				{
					dist[adjNode->vertexNumber]=dist[vertexDistMin] + adjNode->HighwayDist;
					cost[adjNode->vertexNumber]=cost[vertexDistMin] + adjNode->HighwayCost;
				}
					
				else if(dist[adjNode->vertexNumber]==(dist[vertexDistMin] + adjNode->HighwayDist) )
				{ //注意,当dist相同时,比较cost
					if(cost[adjNode->vertexNumber]>cost[vertexDistMin]+adjNode->HighwayCost)
						cost[adjNode->vertexNumber]=cost[vertexDistMin]+adjNode->HighwayCost;
				}				
			}
			adjNode=adjNode->Next;
		}
				
		vertexDistMin=Null;
		distMin=maxValue;
	}
	return;
}

3. 完整编码

  1. 本文如果对你有帮助,请点赞鼓励 ,谢谢 😊
  2. 如有建议或意见,欢迎留言
#include <cstdlib>
#include <iostream>
using namespace std; 

#define maxValue 501
#define max 500
#define Null -1

typedef int vertex;
typedef int weight;

struct EdgeStructure
{
	vertex V1;
	vertex V2;
	weight HighwayDist;
	weight HighwayCost;
};
typedef struct EdgeStructure *PtrEdge;

typedef struct AdjNodeStructure *PtrAdjNode;
struct AdjNodeStructure
{
	vertex vertexNumber;
	weight HighwayDist;
	weight HighwayCost;	
	PtrAdjNode Next;
};

struct HeadNode
{
	PtrAdjNode AdjNode;
};
typedef struct HeadNode HeadNodeArray[max];

struct GraphStructure
{
	int VertexNumber;
	int EdgeNumber;
	HeadNodeArray headNode;
};
typedef struct GraphStructure *ptrGraph;

weight dist[max];
weight cost[max];
bool visited[max];

ptrGraph Graph;
vertex source=maxValue,destination=maxValue;

void buildGraph();
void insertEdge(PtrEdge Edge);
void createNoEdgeGraph(int vertexNumber);

void Dijkstra(vertex source);
void init();

int main()
{
	init();
	
	buildGraph();
	Dijkstra(source);
	cout<<dist[destination]<<" "<<cost[destination];
	
	return 0;
}
void init()
{
	for(int i=0;i<max;i++)
	{
		dist[i]=maxValue;
		cost[i]=maxValue;
		visited[i]=false;
	}
}

void Dijkstra(vertex source)
{  
	PtrAdjNode adjNode=NULL;
	vertex vertexDistMin=Null;
	weight distMin=maxValue;
	
	visited[source]=true; //将源点收入集合S中
	adjNode=Graph->headNode[source].AdjNode;
	
	while(adjNode) //收入后更新源点S的邻接点dist(距离源点的距离) 和 cost(距离源点的路径)
	{
		if( !visited[adjNode->vertexNumber])
		{
			dist[adjNode->vertexNumber]=adjNode->HighwayDist;
			cost[adjNode->vertexNumber]=adjNode->HighwayCost;
		}
		adjNode=adjNode->Next;		
	}
	
	while(true) 
	{
		for(int i=0;i<Graph->VertexNumber;i++) //找到未收录的dist最小的顶点
		{
			if(distMin>dist[i] && !visited[i])
			{
				distMin=dist[i];
				vertexDistMin=i;
			}
		}	
		
		if(vertexDistMin==Null) break;	
		
		visited[vertexDistMin]=true; //收录该顶点
		
		adjNode=Graph->headNode[vertexDistMin].AdjNode;
		while(adjNode) //收入后更新该顶点的邻接点dist(距离源点的距离) 和 cost(距离源点的路径)
		{
			if(!visited[adjNode->vertexNumber])
			{
				if(dist[adjNode->vertexNumber] > (dist[vertexDistMin] + adjNode->HighwayDist))
				{
					dist[adjNode->vertexNumber]=dist[vertexDistMin] + adjNode->HighwayDist;
					cost[adjNode->vertexNumber]=cost[vertexDistMin] + adjNode->HighwayCost;
				}
					
				else if(dist[adjNode->vertexNumber]==(dist[vertexDistMin] + adjNode->HighwayDist) )
				{ //注意,当dist相同时,比较cost
					if(cost[adjNode->vertexNumber]>cost[vertexDistMin]+adjNode->HighwayCost)
						cost[adjNode->vertexNumber]=cost[vertexDistMin]+adjNode->HighwayCost;
				}				
			}
			adjNode=adjNode->Next;
		}
				
		vertexDistMin=Null;
		distMin=maxValue;
	}
	return;
}

void buildGraph()
{
	int VertexNumber;
	PtrEdge Edge; 
	
	cin>>VertexNumber;
	createNoEdgeGraph(VertexNumber);
	cin>>Graph->EdgeNumber;
	cin>>source>>destination;
	
	for(int i=0;i<Graph->EdgeNumber;i++)
	{
		Edge=(PtrEdge)malloc(sizeof(struct EdgeStructure));
		cin>>Edge->V1>>Edge->V2;
		cin>>Edge->HighwayDist>>Edge->HighwayCost;
		insertEdge(Edge);
	}
	return;
}

void insertEdge(PtrEdge Edge)
{	
	PtrAdjNode Node;
	Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
	Node->Next=Graph->headNode[Edge->V1].AdjNode;
	Node->vertexNumber=Edge->V2;
	Node->HighwayDist=Edge->HighwayDist;
	Node->HighwayCost=Edge->HighwayCost;
	Graph->headNode[Edge->V1].AdjNode=Node;
	
	Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
	Node->Next=Graph->headNode[Edge->V2].AdjNode;
	Node->vertexNumber=Edge->V1;
	Node->HighwayDist=Edge->HighwayDist;
	Node->HighwayCost=Edge->HighwayCost;
	Graph->headNode[Edge->V2].AdjNode=Node;
	
	return;
}

void createNoEdgeGraph(int vertexNumber)
{
	Graph=(ptrGraph)malloc(sizeof(struct GraphStructure));
	Graph->VertexNumber=vertexNumber;
	Graph->EdgeNumber=0;
	
	for(int i=0;i<Graph->VertexNumber;i++)
		Graph->headNode[i].AdjNode=NULL;
		
	return ;
}

四、参考

  1. 浙江大学 陈越、何钦铭老师主讲的数据结构
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值