之前自己看过和总结过相关的东西,今天再总结说一下;
感觉权值问题,是否无向有向,权值正负可以直接用不同的算法进行解决;
这次还是主要说一说四种算法:迪杰斯特拉算法、BF算法、SPFA算法、弗洛伊德算法;
迪杰斯特拉算法:
老生常谈最简单的一种算法,从某一点出发,建立Distance数组,不断地寻找当前最近节点,并且选择该节点后,以该节点作为中介,更行其他的全不distance长度;
个人认为适合单源到多点的路径问题;
之前做的总结https://blog.csdn.net/InNoVaion_yu/article/details/86629313
但是仍然需要注意的是该算法的衍生问题:多路径问题;
对于多路径问题往往加限定条件;
不加任何条件的整体代码:
#include<stdlib.h> #include<stdio.h> #include<algorithm> #include<string> #include<iostream> #include<vector> using namespace std; using std::vector; const int MAXV=1000; const int INF=1000000000; struct Node{ int v,dis; }; vector<Node>Adj[MAXV];//图的存储 int d[MAXV];//储存点的距离 bool vis[MAXV]={false};//访问判定 void Dijkstra(int s){ fill(d,d+MAXV,INF);//距离初始化; d[s]=0;//开始访问的点; for(int i=0;i<n;i++){ int u=-1,MIN=INF; for(int j=0;j<n;j++){ if(vis[j]==false&&d[j]<MIN){ //目的是找到邻近中距离最小的相邻点; u=j; MIN=d[j]; } } if(u==-1) return; vis[u]=true; for(int v=0;v<n;v++){ //进行d[v]的优化; if(vis[v]==false&&d[u]+Adj[u][j].dis<d[v]){ d[v]=d[u]+Adj[u][j].dis; } } } } int main(){ system("pause"); }
特殊条件1:
但是迪杰斯特算法往往并不只要求最短距离,也往往会让输出一条最短的路径,如果使用DFS确实是很好解决,但是对于迪杰斯特拉算法,需要额外开一个数组来进行记录,类似于静态的单叉树数组;
整体代码:
void Dijkstra(int st,int n) { //起始节点,进行初始化; vis[st] = false; for (int i = 0; i < n; i++) path[i] = i; fill(dis, dis + maxn, INF); dis[st] = 0; for (int i = 0; i < n; i++) { //进行n-1次迪杰斯特拉遍历; int index = -1;//最近路径节点; int min = INF; for (int i = 0; i < n; i++) if (vis[i] && dis[i]<min) { //如果由路径且没访问过; index = i; } if (index == -1) return; vis[index] = false; for (int j = 0; j < n; i++) { if (vis[j] && mat[index][j] != INF&& mat[index][j] + dis[index] < dis[j]) { //如果以index做终结点,可以使得到j的距离更小; dis[j] = dis[index] + mat[index][j]; path[j] = path[index]; } } } }
这里在每次找到新的最短路径的时候,都会把新更新的节点指向新的前驱;
此时可以根据递归dfs遍历进行求解,且头节点必定索引和对应的值相同;
void dfs(int st, int v) { if (v == st) { cout << st << endl; return; } dfs(st, path[v]); cout << v << endl; }
特殊条件2:
如果再路径相等的情况下附加新的条件,例如结点权值问题,可以直接在迪杰斯特拉算法判定相等的时候进行处理:
特殊条件3:
当需要统计相同路径长度的时候,此时需要数组辅助.但是特别需要注意的是,数组内的意义;
开辟一个num[]数组,其中num[i]代表从初始节点到该节点有几条路;
for (int j = 0; j < n; i++) { if (vis[j] && mat[index][j] != INF) { if (mat[index][j] + dis[index] < dis[j]) { dis[j] = dis[index] + mat[index][j]; num[j] = num[index]; } else if (mat[index][j] + dis[index] == dis[j]) { //当逻辑相等的时候进行逻辑在判断 num[j] += num[index]; } } } }
在初始化的时候,将num[st]=1,其余为0.当出现相同路径的时候,就会把中间节点有几条路径加起来.这个要注意理解,可以画个图自己看一看.言语不太好描述.
Floyd算法:
弗洛伊德算法更像是从矩阵来运算,但是时间复杂度比较高,适合用于邻接链表;
基本思想:在Floyd算法内,dis数组为2维,首先对于一对点u,v,进行中间节点x的枚举.如果出现了:
d[u][v]>d[u][x]+d[x][v],则说明两者间的路径应该经过x.
所以可以看到,大致时间复杂度应该在O(n^3)左右;
主体函数如下所示:
void floyd() { for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (dis[i][k] != INF && dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j]) { dis[i][j] = dis[i][k] + dis[k][j]; } } } } }
注意点:
1.注意所有dis数组初始化都要变为INF,这样比较接近正无穷的实际含义;
2.在进行中间节点距离更新的时候,若d[i][j]中没有路径,表示为正无穷,可以直接进行更新,但是i-k和k-j之间没有油路径一定要进行判定;
Bellman-Ford算法:
考的可能性不太大,考前看一看,喝前摇一摇,毕竟到现在也没见到PAT有这方面的题;
自己之前写的https://blog.csdn.net/InNoVaion_yu/article/details/86647091
来源:https://www.cnblogs.com/songlinxuan/p/12371464.html