图的最短路径问题

<pre name="code" class="cpp">#include <iostream>
#include <cassert>
#include <vector>
#include <stack>
#include <functional>
#include <iterator>

using namespace std;

/*左右的图都使用邻接矩阵(vector<vector<int>>)的表示方法*/


/*Dijkstra算法:设图的顶点集合为U,已确定到原点s的最短路径的顶点集合为S。每次在U-S中选择一个离s最近的顶点v(dist[v]最小)加入S,接着遍历U-S中所有顶点,如果其经过v到达s
更近,那么更新其到s的最短距离dist[i]*/
/*s为源点,dist[i]为顶点i到s的最短距离,prev[i]保存到顶点i到s的最短路径的前驱顶点*/
//dijkstra算法只支持权值为正的图
void dijkstra(const vector<vector<int>>& graph, int s, vector<int>& dist, vector<int>& prev)
{
	size_t nVertex = graph.size();
	dist.resize(nVertex);
	prev.resize(nVertex);
	vector<bool> isVisited(nVertex, false);
	for (size_t i = 0; i < nVertex; ++i)//初始化dist[i]为顶点i到s的距离
	{
		dist[i] = graph[s][i];
		if (dist[i] != INT_MAX)
		{
			prev[i] = s;//如果i与s相邻则prev[i]=s
		}
		else
		{
			prev[i] = -1;//如果i与s不相邻则prev[i]=-1
		}
	}

	isVisited[s] = true;//在集合S中的顶点都会被标记
	prev[s] = -1;

	//每次循环,S中会增加一个顶点,所以只要循环nVertex-1次
	int count = nVertex - 1;
	while (count-- > 0)
	{
		int selected = -1, minDistance = INT_MAX;
		//找出距离s最近的顶点selected
		for (size_t i = 0; i < nVertex; ++i)
		{
			if (!isVisited[i])
			{
				if (dist[i] < minDistance)
				{
					minDistance = dist[i];
					selected = i;
				}
			}
		}
		isVisited[selected] = true;//将selected加入集合S
		//
		//更新dist,看看U-S中的顶点经过selected到达s是不是更短
		//很傻瓜的做法就是对于U-S中的一个顶点,遍历它经过S中的每个顶点到大s会不会更短,然而并不需要遍历S中的每个点,只需要判断
		//刚加入的那个顶点就好了,因为更早前加入S的顶点已经被判断过了
		for (size_t j = 0; j < nVertex; ++j)
		{
			if (!isVisited[j])
			{
				if (graph[selected][j] != INT_MAX && dist[j] > dist[selected] + graph[selected][j])
				{
					dist[j] = dist[selected] + graph[selected][j];
					prev[j] = selected;
				}
			}
		}
	}
}

void printRoute(const vector<int>& prev, const vector<int>& dist, int s)
{
	for (size_t i = 0; i < prev.size(); ++i)
	{
		if (i != s)
		{
			for (int j = i; j != -1; j = prev[j])
			{
				cout << j << "  ";
			}
			cout << endl;
		}
	}
}


/*最小生成树prim算法:设图的顶点集合为U,最小生成树集合为S,任取一个顶点加入到S。每次在U-S中选择一个离S最近的顶点v加入S,直到U-S为空*/
int prim(const vector<vector<int>>& graph, vector<int>& route)//返回最小生成树的边缘权值和
{
	size_t nVertex = graph.size();
	vector<bool> isVisited(nVertex, false);
	vector<int> dist(nVertex);//dist[i]表示顶点i距离集合S最近距离(距离集合S中所有点的最近距离)
	isVisited[0] = true;
	route.reserve(nVertex);
	route.push_back(0);
	dist[0] = 0;
	for (size_t i = 1; i < nVertex; ++i)
	{
		dist[i] = graph[0][i];
	}
	int cost = 0;
	//每次循环,S中会增加一个顶点,所以只要循环nVertex-1次
	int count = nVertex - 1;
	while (count-- > 0)
	{
		int selected = -1, minDistance = INT_MAX;
		//找出距离集合S最近的顶点
		for (size_t i = 0; i < nVertex; ++i)
		{
			if (!isVisited[i])
			{
				if (dist[i] < minDistance)
				{
					minDistance = dist[i];
					selected = i;
				}
			}
		}
		cost += minDistance;
		isVisited[selected] = true;
		route.push_back(selected);
		//
		//更新dist,在U-S中找出距离集合S最近的顶点(距离集合中每个元素最近距离),只要比较每个顶点与刚加入的顶点即可,因为U-S中顶点与S
		//中其它顶点的最短距离早前已经计算过
		for (size_t j = 0; j < nVertex; ++j)
		{
			if (!isVisited[j])
			{
				dist[j] = dist[j] > graph[selected][j] ? graph[selected][j] : dist[j];
			}
		}
	}

	return cost;
}

/*floyd解决任意两点之间的最短距离,动态规划,
for k in vertex 
	if Dis(i,k) + Dis(k,j) < Dis(i,j) 
		Dis(i,j) = Dis(i,k) + Dis(k,j)
这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离*/
void floyd(const vector<vector<int>>& graph, vector<vector<int>>& dists, vector<vector<int>>& path)
{
	//path[i][j]存储最短路径i->j中顶点j的前驱
	size_t n = graph.size();
	path.resize(n);
	for (size_t i = 0; i < n; ++i)
	{
		path[i].resize(n);
		for (size_t j = 0; j < n; ++j)
		{
			if (graph[i][j] == INT_MAX)
			{
				path[i][j] = -1;
			}
			else {
				path[i][j] = i; //初始化为i,因为 
			}
		}
	}
	std::copy(graph.begin(), graph.end(), std::back_inserter(dists));

	for (int k = 0; k < n; ++k)//k一定要放在最外面
	{
		for (int i = 0; i < n; ++i)
		{
			for (int j = 0; j < n; ++j)
			{
				if (dists[i][k] != INT_MAX && dists[k][j] != INT_MAX && dists[i][k] + dists[k][j] < dists[i][j])
				{
					dists[i][j] = dists[i][k] + dists[k][j];
					path[i][j] = k;
				}
			}
		}
	}
}

void printPath(const vector<vector<int>>& path, int from, int to)
{
	if (path[from][to] == -1)
	{
		cout << "No path" << endl;
		return;
	}
	
	if (path[from][to] != from)
	{
		printPath(path, from, path[from][to]);
	}
	cout << path[from][to] << "->" << to << " ";
}

int _tmain(int argc, char* argv[])
{	
	int a[] = {0, 13, 8, INT_MAX, 30, INT_MAX, 32,
		INT_MAX, 0, INT_MAX, INT_MAX, INT_MAX, 9, 7, 
		INT_MAX, INT_MAX, 0, 5, INT_MAX, INT_MAX, INT_MAX, 
		INT_MAX, INT_MAX, INT_MAX, 0, 6, INT_MAX, INT_MAX, 
		INT_MAX, INT_MAX, INT_MAX, INT_MAX, 0, 2, INT_MAX, 
		INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, 0, 17, 
		INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, 0};
	int* p = a;
	//输入一个图
	int nRows = 7, nCols = 7;
	vector<vector<int>> graph(nRows, vector<int>(nCols));
	for (size_t i = 0; i < nRows; ++i)
	{
		for (size_t j = 0; j < nCols; ++j)
		{
			graph[i][j] = *(p++);
		}
	}

	vector<int> dist, prev;
	dijkstra(graph, 0, dist, prev);
	printRoute(prev, dist, 0);
	
	vector<int> route;
	cout << "cost of prim = " << prim(graph, route) << endl;
	cout << "prim = " << endl;
	for (auto r : route)
	{
		cout << r << "  ";
	}
	cout << endl;

	cout << "path of route floyd" << endl;
	vector<vector<int>> dists, path;
	floyd(graph, dists, path);

	printPath(path, 4, 6);

	system("pause");
	return 0;
}



                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值