最短路径法---Dijkstra---迪杰斯特拉算法---图

找最短生成路径

在这里插入图片描述
给一个起点,求到其余各点的最短路径。

在这里插入图片描述

但是注意到,邻接矩阵的主对角线可以作为访问数组

就只需要一个距离值表即可


为了输出路径,加一个path数组

这里的path数组类似并查集

可以直接找到自己的父节点。
下标存该节点的位置。数组的值存(仅一层)父节点的位置。

第一步: 找到最短的路径(可以直接到达),记录位置和到该点的距离

第二步:找到该点之后,标记已找到,找该点到达其他(未走过的)点的最短路径,找到则更新位置,并记录中转结点。

template <class T, class E>
void Graphmtx<T, E>::shortestPath(int v0, int *path, int* dist)
{
	int i = 0 ,k = 0 ,j= 0 ,pos = 0;
	int min = 9999;
	for (i = 0; i < numVertices; i++)
	{
		dist[i] = Edge[v0][i];                      //把邻接矩阵一行复制到dist数组中
		if (  i!=v0 &&  Edge[v0][i] < maxWeight)    //有边则有路径
			path[i] = v0;
		else
			path[i] = -1;
	}
	Edge[v0][v0] = 1; //访问标志
	for (i = 0; i < numVertices - 1; i++)
	{
		min = 9999; //每次都要更新min  
		for (k = 0; k < numVertices; k++)
		{
			//找最小值和它所在的位置
			if ( Edge[k][k] == 0 && dist[k] < min )  //标记过的结点不会再出现
			{
				pos = k;
				min = dist[k];
			}
		}
		//找到之后,得到位置pos
		Edge[pos][pos] = 1;
		for (j = 0; j < numVertices; j++)
		{
			if ((Edge[j][j] == 0) && Edge[pos][j] + min < dist[j])
			{
				dist[j] = Edge[pos][j] + min; //更新最小路径
				path[j] = pos;
			}
		}
	}
}

标记 的最终 的 path数组展示

在这里插入图片描述

下面是全部代码


#include<iostream>
#include"Queue.h"
#include "Minheap.h"
using namespace std;
//图的邻接矩阵模式
extern const int maxWeight = 9999;
const int maxSize = 100;

//邻接矩阵存储的图的类定义(无向图)
template <class T, class E>
class Graphmtx 				         //图的邻接矩阵类定义
{
	friend istream& operator >>(istream& in, Graphmtx<T, E>& G);
	friend ostream& operator <<(ostream& out, Graphmtx<T, E>& G);
public:
	int maxWeightG;
	Graphmtx(int sz);						//构造函数
	~Graphmtx()										//析构函数
	{
		delete[]VertexList;
		delete[]Edge;
	}
	T getValue(int i)								//取顶点i的值, i不合理返回0
	{
		return i >= 0 && i < numVertices ? VertexList[i] : 0;
	}
	E getWeight(int v1, int v2) 					//取边(v1,v2)上的权值,若边不合理,则返回0
	{
		return v1 != -1 && v2 != -1 ? Edge[v1][v2] : 0;
	}
	//取顶点v的第一个邻接顶点//返回该邻接顶点的编号,若不存在则返回-1
	int getFirstNeighbor(int v)
	{
		if (v<0 || v>numVertices)
		{
			cout << "下标有误,不存在该顶点"; return -1;
		}//不存在该顶点
		for (int i = 0; i < numVertices; i++)
		{
			if (Edge[v][i] > 0 && Edge[v][i] < 9999)
			{
				return i;
			}
		}
	}
	//取v的邻接顶点w的下一邻接顶点//返回下一个邻接定点的编号,若不存在或参数不合理则返回-1
	int getNextNeighbor(int v, int w)
	{
		if (v != -1 && w != -1)
		{
			for (int col = w + 1; col < numVertices; col++)
			{
				if (Edge[v][col] > 0 && Edge[v][col] < 9999)
				{
					return col;
				}
			}
		}
		return -1;  //不合理返回 -1

	}
	//插入顶点vertex
	bool insertVertex(const T& vertex)
	{												//接受一个参数,表示插入顶点的值,返回true表示插入成功
		if (numVertices == maxVertices) return false;
		VertexList[numVertices++] = vertex;
		return true;
	}
	//插入边(v1, v2),权值为cost//返回true表示插入成功
	bool insertEdge(int v1, int v2, E cost)
	{
		if (v1 > -1 && v1 < numVertices && v2>-1 && v2 < numVertices && Edge[v1][v2] == maxWeight)
		{
			Edge[v1][v2] = cost;
			numVertices++;
			return true;
		}
		else
			return false;
	}
	//删去顶点v和所有与它相关联的边
	bool removeVertex(int v)
	{
		if (v<0 || v>maxWeight) return false;
		if (numVertices == 1) return false;
		int i = 0, j = 0;
		for (i = 0; i < numVertices; i++)  //减去有关的边
		{
			if (Edge[i][v] > 0 && Edge[i][v] < maxWeight) numEdges--;
		}
		//用最后一个顶点覆盖要移除的顶点
		for (i = 0; i < numVertices; i++)
		{
			Edge[i][v] = Edge[i][numVertices - 1];//用最后一列填补删除的那一列
		}
		for (j = 0; j < numVertices; j++)
		{
			Edge[v][j] = Edge[numVertices - 1][j];//用最后一行填补删除的那一行
		}
		return true;
	}
	//在图中删去边(v1,v2)
	bool removeEdge(int v1, int v2)
	{
		if (v1 > -1 && v1 < numVertices && v2>-1 && v2 < numVertices && Edge[v1][v2] < maxWeight && Edge[v1][v2] > 0)
		{
			Edge[v1][v2] = Edge[v2][v1] = maxWeight;
			numEdges--;
			return true;
		}
		else
			return false;
	}
	int NumberofVertices()
	{
		return numVertices;
	}
	int NumberofEdges()
	{
		return numEdges;
	}
	int getVertexPos(T v)
	{
		for (int i = 0; i < numVertices; i++)
		{
			if (VertexList[i] == v)
			{
				return i;
			}
		}
		return -1;
	}

	void DFS(Graphmtx<T, E>& G, int v);
	void BFS(Graphmtx<T, E>& G, int k);
	void locata(T& vex1, T& vex2, int& m, int& n);
	void getGraphmtx();
	T getVertical(int x)
	{
		return VertexList[x];
	}
	void putEdge(int x) {
		if (x<0 || x>numEdges) { cout << "下标有误 " << endl;  return; }
		Edge[x][x] = 1;
	}
	int outEdge(int x)
	{
		if (x<0 || x>numEdges) { cout << "下标有误 " << endl;  return -1; }
		return Edge[x][x];
	}
	void shortestPath(int v0, int* dist, int* path);
	void print_path(int u, int x, int* path, int* dist);
private:
	int maxVertices;   //图中最大顶点数
	int numEdges;
	int numVertices;
	T* VertexList; 	 //顶点表(点集)
	E** Edge;		 //邻接矩阵, 数组的元素值为两个顶点之间的权值。(边集)
	bool* visited;   //遍历标记
};

template <class T, class E>
istream& operator >>(istream& in, Graphmtx<T, E>& G)
{
	int i = 0, j = 0, k = 0, n = 0, m = 0;
	E weight;    //权重
	T e1, e2;  //顶点
	cout << "输入顶点数和边数" << endl;
	in >> n >> m;  //输入顶点数和边数
	for (i = 0; i < n; i++)
	{
		cin >> e1;  G.insertVertex(e1);
	}
	i = 0;
	while (i < m)
	{
		cout << "请输入两个顶点以及顶点之间的权重" << endl;
		in >> e1 >> e2 >> weight;
		j = G.getVertexPos(e1); k = G.getVertexPos(e2);
		if (j == -1 || k == -1)
		{
			cout << "边的两端点信息有误" << endl;
		}
		else
		{
			G.insertEdge(j, k, weight);
			i++;
		}
	}
	return in;
}

template <class T, class E>
ostream& operator <<(ostream& out, Graphmtx<T, E>& G)
{
	int i = 0, j = 0, k = 0, n = 0, m = 0;
	E weight;    //权重
	T e1, e2;  //顶点
	n = G.NumberofVertices();
	m = G.NumberofEdges();
	out << "顶点数  " << n << "," << " 边数  " << m << endl;
	for (i = 0; i < n; i++)
	{
		for (j = i + 1; j < n; j++)
		{
			weight = G.getWeight(i, j);
			if (weight > 0 && weight < maxWeight)
			{
				e1 = G.getValue(i);
				e2 = G.getValue(j);
				out << "(" << e1 << "," << e2 << ")" << endl;
			}
		}
	}
	return out;
}
//构造函数
template <class T, class E>
Graphmtx<T, E>::Graphmtx(int sz)
{
	int i = 0, j = 0;
	maxVertices = sz;
	maxWeightG = maxWeight;
	numVertices = 0;  //初始化顶点数为0,后面加入时会累次递增
	numEdges = 0;
	cout << "请输入顶点的数量" << endl;
	cin >> numVertices;
	cout << "请输入边的数量" << endl;
	cin >> numEdges;
	VertexList = new T[maxVertices];  //顶点只需要一维的矩阵存就可以。
	Edge = (int**) new int* [maxVertices];		//创建邻接矩阵数组
	visited = new bool[maxVertices];

	cout << "输入顶点的值" << endl;
	for (i = 0; i < numVertices; i++)
	{
		cin >> VertexList[i];
	}
	for (i = 0; i < maxVertices; i++)
	{
		visited[i] = false;
	}
	for (i = 0; i < maxVertices; i++)
	{
		Edge[i] = new int[maxVertices];
	}
	for (i = 0; i < maxVertices; i++)//浪费空间的做法,需要一开始就开很大的空间。
	{
		for (j = 0; j < maxVertices; j++)
		{
			Edge[i][j] = (i == j) ? 0 : maxWeight;  //除了对角线为0,其余都是无穷大(权值)带权图
			//不带权的图可以直接全部初始化为0 
		}
	}
	getGraphmtx();
}
//创建图(以邻接矩阵的方式)
//通过结点信息(A,B,C,D之类)来查找对应矩阵下标
//确定边在矩阵的位置

template <class T, class E>
void Graphmtx<T, E>::locata(T& vex1, T& vex2, int& m, int& n)
{
	for (int i = 0; i < maxVertices; i++)
	{
		if (vex1 == VertexList[i])
			m = i;
		if (vex2 == VertexList[i])
			n = i;
	}
}

//创建图对应的邻接矩阵
template <class T, class E>
void Graphmtx<T, E>::getGraphmtx()
{
	E weight;
	//根据输入的两个顶点,来更新矩阵的值(如输入A,B表示从A到B有一条边,就更新矩阵相应位置的值为1)
	int i = 1;
	cout << numEdges << endl;
	while (i <= numEdges)
	{
		T vex1, vex2; //接受输入的两个顶点,来表示从vex1到vex2这条边
		cout << "请输入边的信息(形如A B,表示从A到B的一条边)以及权重:";
		cin >> vex1 >> vex2 >> weight;
		int m = 0, n = 0;        //m和n是用来接受vex1和vex2所在的下标值,好据此更新矩阵相应位置的值
		locata(vex1, vex2, m, n);
		Edge[m][n] = weight;
		Edge[n][m] = weight;//无向图邻接矩阵对称,少掉这句代码就是有向图的邻接矩阵了
		i++;
	}
}

template <class T, class E>
void Graphmtx<T, E>::DFS(Graphmtx<T, E>& G, int v)   //输入图,和当前顶点的位置
{
	int j = 0;
	G.visited[v] = true;
	cout << G.VertexList[v] << "-> ";
	for (j = 0; j < G.numVertices; j++)
		if (G.Edge[v][j] > 0 && G.Edge[v][j] < maxWeightG && !visited[j])          //v表示其中一个点的下标为(0-4),j表示另一个点的下标为(0-4)
			//该判断是邻接点 且未被访问过的点
			DFS(G, j);
}
/*
//遍历递归,需要参数v的
//DFS=====类似前序遍历,就可以看成前序遍历(根左右)
template<class T, class E>
void Graph<T, E>::DFS(int v)                   //v表示起点
{
	Edge<T, E> *p;
	visited[v] = true;
	cout << NodeTable[v].data<<" -> ";
	p = NodeTable[v].adj;
	while (p != 0)
	{
		if (!visited[p->dest])
		DFS(p->dest);//先把一个点的路走到头, 再找其他点作为起点
		p = p->link;
	}
}

*/
//BFS用队列
//图可以看成n叉树,每一层都可以有n个子结点
//每次调用函数前都要设置visited矩阵为0
template <class T, class E>
void Graphmtx<T, E>::BFS(Graphmtx<T, E>& G, int k) //输入图,和当前顶点的位置
{
	int i = 0;
	for (i = 0; i < maxVertices; i++)
	{
		visited[i] = false;
	}
	SeqQueue<int> q;
	q.EnQueue(k);
	G.visited[k] = 1;
	i = 0;
	while (!q.IsEmpty())
	{
		i = q.getFront(i);
		q.DeQueue(i);
		cout << G.VertexList[i] << "-> ";
		for (int j = 0; j < G.numVertices; j++) {
			if (G.Edge[i][j] > 0 && G.Edge[i][j] < maxWeightG && G.visited[j] == 0)//把所有子结点入队(或者理解为把所有相邻结点且未访问过的入队)
			{
				G.visited[j] = 1;
				q.EnQueue(j);
			}
		}
	}
}

template <class T, class E>
void Graphmtx<T, E>::shortestPath(int v0, int *path, int* dist)
{
	int i = 0 ,k = 0 ,j= 0 ,pos = 0;
	int min = 9999;
	for (i = 0; i < numVertices; i++)
	{
		dist[i] = Edge[v0][i];                      //把邻接矩阵一行复制到dist数组中
		if (  i!=v0 &&  Edge[v0][i] < maxWeight)    //有边则有路径
			path[i] = v0;
		else
			path[i] = -1;
	}
	Edge[v0][v0] = 1; //访问标志
	for (i = 0; i < numVertices - 1; i++)
	{
		min = 9999; //每次都要更新min  
		for (k = 0; k < numVertices; k++)
		{
			//找最小值和它所在的位置
			if ( Edge[k][k] == 0 && dist[k] < min )  //标记过的结点不会再出现
			{
				pos = k;
				min = dist[k];
			}
		}
		//找到之后,得到位置pos
		Edge[pos][pos] = 1;
		for (j = 0; j < numVertices; j++)
		{
			if ((Edge[j][j] == 0) && Edge[pos][j] + min < dist[j])
			{
				dist[j] = Edge[pos][j] + min; //更新最小路径
				path[j] = pos;
			}
		}
	}
}

template <class T, class E>
void Graphmtx<T, E>::print_path(int u, int x,int *path, int *dist)
{
	int *a,c=0,ex=x;
	a = new int[numVertices];
	for (int j = 0; j < numVertices; j++)
	{
		a[j] = 0;
 	}
	if (u == x)
		cout << VertexList[u] << "->" << VertexList[x] << "=0"<<endl;
	else if(path[x]==-1)
		cout << VertexList[u] << "->" << VertexList[x] << "="<< dist[x] << endl;
	else
	{
		c = 0;
		while (x != u)
		{
			a[c++] = x;
			x = path[x];
		}
		a[c] = x;
		for (int i = c; i > 0; i--)
		{
			cout << VertexList[a[i]] << " -> ";
		}
		cout << VertexList[a[0]] <<" = "<< dist[ex]<<endl;
	}
	cout << endl;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值