Naive Dijkstra vs Heap Optimized Dijkstra Summary

Naive Dijkstra vs Heap Optimized Dijkstra Summary

1. Naive version of the dijkstraalgorithm

Dijkstra's general idea

That is, perform n (n is the number of n) iterations to determine the minimum value of each point to the starting point and the final output end point is the shortest distance we are looking for

According to this idea, in addition to the need to store the graph (adjacency matrix), these two quantities also need to be stored:

dist[N] //用于存储每个点到起点的最短距离
st[N]   //用于在更新最短距离时 判断当前的点的最短距离是否确定 是否需要更新

In the process of each iteration, we first find the point with the shortest distance among the currently undetermined shortest distance points

int t=-1;//将t设置为-1 因为Dijkstra算法适用于不存在负权边的图
for(int j=1;j<=n;j++){
    
    
    if(!st[j]&&(t==-1||dist[j]<dist[t])){
    
    //寻找还未确定最短路的点中路径最短的点
        t=j;
    }
}

Through the above operation, our current t represents the point with the shortest path among the remaining undetermined shortest points
, and at the same time, the shortest path of the point has also been determined to mark the point

st[t]=true;

Then use this point to update the shortest distance of the remaining undetermined points

for(int j=1;j<=n;j++){
    
    
	dist[j]=min(dist[j],dist[t]+g[t][j]);
}

//思考:j如果从1开始的话 会不会影响之前已经确定的点的最小距离?
//不会 因为按照我们的Dijkstra算法的操作顺序 先确定最短距离的点的距离已经比后确定的要小 所以不会影响

After n iterations, you can finally determine the shortest distance of each point,
and then output the corresponding shortest distance according to the meaning of the question.

The following is AcWing849. Dijkstra finds the shortest path I

Topic source: https://www.acwing.com/problem/content/851/

#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;

const int N=510;

int g[N][N];//为稠密阵所以用邻接矩阵存储
int dist[N];//用于记录每一个点距离第一个点(起点)的距离
bool st[N]; //用于记录该点的最短距离是否已经确定
int n,m;

int dijkstra(){
    
    
    memset(dist,0x3f,sizeof dist);//初始化距离  0x3f代表无限大
    dist[1]=0;//第一个点到自身的距离为0
    
    for(int i=0;i<n;i++){
    
    //有n个点所以要进行n次 迭代
        int t=-1; //t存储当前访问的点
        for(int j=1;j<=n;j++){
    
    //这里的j代表的是从1号点开始
            if(!st[j]&&(t==-1||dist[j]<dist[t])){
    
    
                t=j;
            }
        }
        st[t]=true;
        
        for(int j=1;j<=n;j++){
    
    //依次更新每个点所到相邻的点路径值
            dist[j]=min(dist[j],dist[t]+g[t][j]);
        }
    }
    if(dist[n]==0x3f3f3f3f)return -1;//如果第n个点路径为无穷大即不存在最低路径
    return dist[n];
}
int main(){
    
    
    memset(g,0x3f,sizeof g);//初始化图 因为是求最短路径 所以每个点初始为无限大
    scanf("%d%d",&n,&m);
    
    while(m--){
    
    
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        g[a][b]=min(c,g[a][b]);//如果发生重边的情况则保留最短的一条边
    }
    
    cout<<dijkstra()<<endl;
    
    return 0;
}

The summary of this paragraph refers to the problem solution and summary written by the author Ni in Acwingthe shortest problem.

The address is as follows: https://www.acwing.com/solution/content/5806/


2. Heap Optimized dijkstraAlgorithm

Why heap optimization, first look at the difference in time complexity between the heap optimized version and the naive version

Time complexity analysis of the naive version
Find the point with the shortest path: O(n^2)
Join the set S: O(n)
Update distance: O(m)

Time complexity analysis of heap optimization version
Find the point with the shortest path: O(n)
Join the set S: O(n)

Heap insertion operation: O(log(n))

Update distance: O(mlog(n))

Partly borrowed from: https://www.acwing.com/solution/content/6554/

The time complexity of the naive version is O(n^2)

Heap optimized version time complexity O(mlog(n))


chained forward star

Before learning the heap-optimized version of Dijkstra's algorithm, you first need to know what a forward star is

The basic definition and implementation of forward star can be seen from blogger: Shui Wu Yin's blog C++ chain forward star

Using the forward star will have a sorting operation, and if you use a quick sort, the time is at least O(nlog(n))

Sorting can be avoided if chained forward stars are used.

Build the structure:

struct Edge
{
    
    
     int next;
     int to;
     int w;
};

edge[i].to represents the end point of the i-th edge

edge[i].next indicates the storage location of the next edge that has the same origin as the i-th edge

edge[i].w is the edge weight

int head[];

There is also an array head[], which is used to represent the location where the first edge starting from i is stored

In fact, the location where the first edge is stored here is actually the number entered at the end of all edges starting from i

The head[] array is generally initialized to -1

memset(head,-1,sizeof head);

The add function for adding edges

void add(int x,int y,int w)
{
    
    
	edge[cnt].w=w;
	edge[cnt].to=y;
	edge[cnt].next=head[x];
	head[x]=cnt++;
}

initializationcnt=0;

Simulation and Interpretation

[External link image transfer failed, the origin site may have anti-leech mechanism, it is recommended to save the picture failed, the origin site may have anti-leech mechanism, it is recommended to save the picture and upload it directly (iQyX1gs1YMr7-1615090335941) (C:\Users\ 86182\AppData\Roaming\Typora\typora-user-images\image-20210307100559687.png)(C:\Users\86182\AppData\Roaming\Typora\typora-user-images\image-20210307100559687.png)]

The order of our input edges is:

1 2

2 3

3 4

1 3

4 1

1 5

4 5

Simulate the process according to the diagram and input

edge[0].to=2;edge[0].next=-1;head[1]=0;

edge[1].to=3;edge[1].next=-1;head[2]=1;

edge[2].to=4;edge[2].next=-1;head[3]=2;

edge[3].to=3;edge[3].next=0;head[1]=3;

edge[4].to=1;edge[4].next=-1;head[4]=4;

edge[5].to=5;edge[5].next=-1;head[1]=5;

edge[6].to=5;edge[6].next=4;head[4]=6;

Obviously, head[i] saves the one with the highest number among all the edges starting from i, and takes this as the position of the first starting edge of vertex i

In this way, the traversal is reversed, that is to say, it is opposite to the input order, but this does not affect the correctness of the result.

Take the above figure as an example, there are 3 edges starting from node 1, their numbers are 0, 3, 5 and head[1]=5

When we traverse all the edges starting from x, this is the case

for(int i=head[x];i!=-1;i=edge[i].next)

That is to say, first traverse the edge numbered 5, which is head[1], then edge[5].next, which is the edge numbered 3, and then edge[3].next, which is numbered 0. , it can be seen that the order is reversed

priority queue

The priority queue has all the characteristics of the queue, including basic operations, but adds an internal sorting on this basis, which is essentially a heap implementation

The basic operation is the same as the queue:

  • top accesses the head element of the queue
  • empty whether the queue is empty
  • size returns the number of elements in the queue
  • push inserts an element to the end of the queue (and sorts it)
  • emplace constructs an element in place and inserts it into the queue
  • pop pops the head of the queue element
  • swap swap content

Definition: priority_queue<Type, Container, Functional> Type is the data type, Container is the container type (Container must be a container implemented with an array, such as vector, deque, etc., but cannot use a list. The default in STL is vector), Functional is a way of comparison. When you need to use a custom data type, you only need to pass in these three parameters. When using a basic data type, you only need to pass in the data type. The default is the big top heap

//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;

This paragraph refers to blogger Lu Bai_'s blog: Detailed explanation of c++ priority queue (priority_queue) usage

Heap-optimized version of dijkstra's ideas

The heap-optimized version of dijkstra is optimized for the naive version of dijkstra. In the naive version of dijkstra, the point with the highest time complexity and the shortest distance O(n^2) can be optimized using the minimum heap.

  1. The distance from the starting point is initialized to zero, and the other points are initialized to infinity.
  2. Put the starting point into the heap.
  3. Keep looping until the heap is empty. The operation performed in each loop is:
    pop the top of the heap (same as the naive version of diijkstra to find the point with the shortest distance outside S, and mark the shortest path to this point has been determined).
    Use this point to update the distance of the critical point, and add it to the heap if the update is successful.

The following is AcWing850. Dijkstra seeks the shortest path II

Topic source: https://www.acwing.com/problem/content/852/

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int N=150010;
typedef pair<int,int> PII;//<离起点的距离,节点编号> 
int to[N],ne[N],w[N],idx;
int h[N];
int dist[N];
int st[N];
int n,m;

//在x节点之后插入一个y节点,权重为w 
void add(int x,int y,int c){
    
    
	w[idx]=c;
	to[idx]=y;
	ne[idx]=h[x];
	h[x]=idx++;
} 

int dijkstra(){
    
    
	memset(dist,0x3f,sizeof dist);//所有距离初始化为无穷大
	dist[1]=0;//1号节点距离为0 
	
	priority_queue<PII,vector<PII>,greater<PII> > heap;//建立一个小根堆  --优先队列
	heap.push({
    
    0,1});//1号节点插入堆
	while(heap.size()){
    
    
		PII t=heap.top();//取出堆顶顶点
		heap.pop();//并从堆中删除
		int ver=t.second,distance=t.first; //取出节点编号和节点距离
		if(st[ver])continue;//如果节点被访问过则跳过
		st[ver]=true;
		//遍历以ver为起始位置的所有边 
		for(int i=h[ver];i!=-1;i=ne[i]){
    
    
			int j=to[i];//取出节点编号
			if(distance+w[i]<dist[j]){
    
     
				dist[j]=distance+w[i];
				heap.push({
    
    dist[j],j});
			} 
		}
	} 
	if(dist[n]==0x3f3f3f3f)return -1;
	return dist[n];
}
int main(){
    
    
	memset(h,-1,sizeof h);//将h[]数组初始化为-1
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
    
    
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
	}
	cout<<dijkstra();
	return 0; 
}

The summary of this paragraph refers to the problem solution and summary written by the author Squirrel Love Grape in Acwingthe shortest problem.

Address: https://www.acwing.com/solution/content/14007/


Summary:
I finally learned (summarized) the most basic algorithm in the shortest path of the unit, Dijkstra algorithm (this algorithm can only be used for graphs where all edge weights are positive numbers), the next article should be Bellman who can solve the problem of negative weight edges -Ford and SPFA algorithm too.

I am also a beginner to learn algorithms. During the learning process, I deeply realized that it is not easy to learn algorithms. While summarizing personally, I also hope that this article can help friends who are learning algorithms like me! If you find that there are mistakes in the article that are not fully written, I hope that the big guys from all walks of life can correct me! Thank you!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325982850&siteId=291194637