[] Algorithm design and analysis dijkstra shortest path algorithm and the bellman-ford algorithm

Disclaimer: This article is a blogger original article, reproduced, please indicate the source https://blog.csdn.net/C2681595858/article/details/85687836


Experiment code (GitHub)

First, the experiment content

1, Dijkstra shortest path algorithm.

  • Test chart
    Here Insert Picture Description
  • expected results:
    Here Insert Picture Description

2, Bellman-ford shortest path algorithm

  • Test chart
    Here Insert Picture Description
  • expected results
    Here Insert Picture Description

Second, the theoretical preparation

1, Dijkstra algorithm

  • The biggest feature is a safeguard for each vertex a \alpha values, is from the origin to that point, through the shortest path length is already known. Then add to the mix has not been selected vertices a \alpha smallest vertex values add to the mix, then add to the mix immediately with the modified vertex adjacent vertices a \alpha value, has been circulating until all vertices have been add to the mix.
  • As long as it has two steps, a vertex is updated undetermined a \alpha value, and the other is selected from the apex of the updated a \alpha minimum value of one vertex, add to the mix, that is, it becomes the vertex determination. Then choose to update the aim is to
  • Pseudo-code as follows:
    Here Insert Picture Description
    • decrease-key () here said to reduce the vertex v key value in the queue.
    • Special attention , where each update priority queue a \alpha [V] is the time a [ u ] + L e \alpha [u]+L_{e} 。学过最小生成树的同学知道有一个prim算法与dijkstra算法非常相似,但是注意他们并不相同,prim算法每次用定点u到v之间的权重去更新优先队列中v的key值,不需要加上 α [ u ] \alpha[u] .

2、bellman-ford算法

  • 算法思路:
    核心思想是:首先对距离进行松弛,然后随着迭代次数的增加,距离越来越接近最短路径,直到最后得出最短路径。
    更具体一点说就是每一次检查每一条边(u,v),看是否有 d [ v ] > d [ u ] + L u v d[v] > d[u] + L_{uv} 情况,如果有就更新d[v]的值,这样一来每一遍大的循环就把源点的情况全局推进一步,然后最多推进n-1步也就把原点的情况推到了每一个节点。
  • 伪码:
    Here Insert Picture Description
  • 伪码解读:
    • 在内部的两层循环中看似是两层循环应该有 m 2 m^2 的复杂度,但是它真正是m的复杂度,这是为什么呢?
    • 首先思考bellman-ford算法最初的想法,他是每次查看一条边是否满足 d [ v ] > d [ u ] + L u v d[v] > d[u] + L_{uv} 情况。所以那两层内层循环实质上是在便利所有的边,所以它的复杂度只是边的条数m,而不是 m 2 m^2 .

3、 两个算法的使用环境。

  • Dijkstra's algorithm is not suitable for drawing negative side of right.
    • Lemma 1: If a ring contains a negative s ~ v, then it is not the shortest path.
    • Lemma 2: If there is no negative laps, then it must be the shortest path and the shortest path comprising up to n-1 edges, n being the number of vertices.
  • bellman-ford may have negative side, but if there is a negative side we must not have a negative circle .
  • Negative circle : on each side of the circle and negative. Here we note that there is a negative side and a negative side in the ring, but the ring is not necessarily a negative circle.

Third, the experimental environment

  • Operating system and version: windows10
  • Compiler software and version: g ++ 6.3.0
  • Computer language used: c language

Fourth, the experiment

1, Dijkstra algorithm

  • Algorithms and theoretical preparation process is the same, the core code is as follows:
void Graph::dijkstra(int startId)
{//先把它从队列中删除,然后找到它的邻居顶点并更新节点的值直到队列为空
	Pritree<Vertex> pritree;
	initialForDij(pritree, startId);
	while(pritree.getSize() != 0)
	{
			Vertex temp =  pritree.popHead();//找到discovery最小的值
			Vertex* vertexp = findVerAccId(temp.getVertexId());
			Node* neighbor = vertexp->getHeadNode();//找到邻居节点
		//	cout<<temp.getVertexId()<<"->  ";
			while(neighbor != NULL)//对每一个邻居节点进行循环中的操作
			{
				Vertex* tempNe = neighbor->getVertex();//与弹出来的顶点相邻的顶点
				if(tempNe->getDiscovery() > temp.getDiscovery() + neighbor->getWeight())
				{
					tempNe->setDiscovery(temp.getDiscovery() + neighbor->getWeight());
					tempNe->setParent(vertexp);//记住其父节点
					pritree.delete_ele(*tempNe, equal);//从优先队列中删除
					pritree.insert(*tempNe);//然后插入新的更新后的顶点
				}
				neighbor = neighbor->getNextNode();
			}
	}
//打印出最后的结果
	Vertex* for_out_ver = this->headVertex;
	cout<<"dijkstra:"<<endl;
	while(for_out_ver != NULL)
	{
		for_out_ver = for_out_ver->getNextVertex();
		if(for_out_ver != NULL)
		  cout<<(for_out_ver->getParent())->getVertexId()<<"->"<<for_out_ver->getVertexId()<<endl;
	}

}

2, bellman-ford algorithm

  • Theoretical preparation and implementation of the pseudo-code is the same, it's the realization of ideas than words dijkstra algorithm should be simple, but its complexity is higher.
void Graph::bellmanFord(int startId)
{
	//初始化
	Vertex* vptr = this->headVertex;
	while(vptr != NULL)
	{
		if(vptr->getVertexId() == startId)
			vptr->setDiscovery(0);
		else
			vptr->setDiscovery(INT_MAX);

		vptr->setParent(NULL);
		vptr = vptr->getNextVertex();
	}

	//最外层迭代顶点数目减一次
	for(int counter0 = 1; counter0 < this->vertexNumber; counter0++)
	{
		vptr = this->headVertex;
		while(vptr != NULL)//遍历每一个顶点
		{
			Node* eptr = vptr->getHeadNode();
			while(eptr != NULL)//遍历每一条边
			{
				Vertex* vtoptr = eptr->getVertex();
				if( vtoptr->getDiscovery()> vptr->getDiscovery()+eptr->getWeight())
				{
					vtoptr->setDiscovery(vptr->getDiscovery()+eptr->getWeight());
					vtoptr->setParent(vptr);
				}
				eptr = eptr->getNextNode();
			}
			vptr = vptr->getNextVertex();
		}
	}

	Vertex* for_out_ver = this->headVertex;
	cout<<"bellman-ford:"<<endl;
	while(for_out_ver != NULL)
	{
		for_out_ver = for_out_ver->getNextVertex();
		if(for_out_ver != NULL)
			cout<<(for_out_ver->getParent())->getVertexId()<<"->"<<for_out_ver->getVertexId()<<endl;
	}

}

Fifth, the experimental results

  • FIG test given by the contents in the experiments, the following is the result of execution of the algorithm, a pair of numbers representative of one edge:
    Here Insert Picture Description
  • Note that the results of these two different algorithms, FIG test is not the same, of course, also be used without the negative side of FIG bellman-ford to test the algorithm.

Six experimental summary

  • When the idea to write dijkstra algorithm is very clear, but there are always write bug, bug really took half an hour to write, debug with for a long time, finally found the reason was not careful enough, mistaken addresses temporary objects deposited into the list, the last access time error. Dynamic priority queue array to achieve, so that the pseudo code for the corresponding subscript and PPT, the position of a dynamic array without first storing things, the last time the write function delete_ele ignored, resulting in a long check.

Guess you like

Origin blog.csdn.net/C2681595858/article/details/85687836