算法运用到了向前星知识,一种边集数组
https://blog.csdn.net/flymoyu/article/details/90319846
Dijkstra 单源最短路---------权值必须非负
const int maxn=1010;
const int inf=0x3f3f3f3f;
bool vis[maxn];
int pre[maxn];
void Dijkstra(int mp[][maxn],int dis[],int n,int start)
{
//初始化
for(int i=0;i<n;i++)
{
dis[i]=inf;
vis[i]=false;
pre[i]=-1;
}
dis[start]=0;
for(int j=0;j<n;j++)
{
//找到最短路径更新
int k=-1;
int minn=inf;
for(int i=0;i<n;i++)
{
if(!vis[i]&&dis[i]<minn)
{
minn=dis[i];
k=i;
}
}
if(k==-1)
break;
vis[k]=true;
for(int i=0;i<n;i++)
{
if(!vis[i]&&dis[k]+mp[k][i]<dis[i])
{
dis[i]=dis[k]+mp[k][i];
pre[i]=k; //路径 记录start到 i 路径上的父亲节点
}
}
}
}
Dijkatra+堆优化
时间复杂度 O(nlong(n))
通过优先队列代替循环找最小值,每次取出的一定是最小值
int dis[maxn];
void Dijkstra(int s)
{
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
priority_queue<int,vector<int>,greater<int>>que; //升序
//priority_queue,less>que;
que.push(make_pair(0,s));
while(!que.empty())
{
int u=que.top().second();
int d=que.top().first();
que.pop();
if(dis[u]<d) continue;
for(int i= head[u];i!=-1;i=e[i].next)
{
v=e[i].v;
w=e[i].w;
if(dis[v]>d+v)
{
dis[v]=d+v;
que.push(make_pair(dis[v],v));
}
}
}
}
Floyd算法–O(nnn)
核心算法+传递闭包
https://blog.csdn.net/flymoyu/article/details/90298395
核心代码:
for (int k = 1; k <= n; k++) {//n为节点个数
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (dis[i][k] + dis[k][j] > dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
Bellman_Ford算法
const int inf=0x3f3f3f3f;
const int maxn=550;
int dis[maxn];
struct Edge
{
int u,v;
int w;
};
vector<Edge>e;
bool bellman_ford(int s,int n)
{
for(int i=1;i<=n;i++) dis[i]=inf;
dis[s]=0;
for(int i=1;i<n;i++)
{
bool flag=false;
for(int j=0;j<e.size();j++)
{
int u=e[j].u;
int v=e[j].v;
int w=e[j].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
flag=true;
}
}
if(!flag) return true;
}
for(int j=0;j<e.size();j++)
{
if(dis[e[j].v]>dis[e[j].u]+e[j].w)
return false;
}
return true;
}
SPFA算法(Bellman_Ford的队列优化)—无法处理带负环的边
算法特点:在 Bellman-ford 算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。
时间复杂度:O(mn)
主要变量如下:
int n 表示有n个点,从1~n标号
int s,t s为源点,t为终点
int dis[N] dis[i]表示源点s到点i的最短路径
int pre[N] 记录路径,pre[i]表示i的前驱结点
bool vis[N] vis[i]=true表示点i在队列中
queue q 队列,在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记
【初始化】
dis数组全部赋值为INF,pre数组全部赋值为-1(表示还不知道前驱),
dis[s] = 0 表示源点不要求最短路径(或者最短路径就是0)。
【队列+松弛操作】
读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队,这样不断从队列中取出顶点来进行松弛操作。
以此循环,直到队空为止就完成了单源最短路的求解。
【算法过程】
设立一个队列用来保存待优化的顶点,优化时每次取出队首顶点 u,并且用 u 点当前的最短路径估计值dis[u]对与 u 点邻接的顶点 v 进行松弛操作,如果 v 点的最短路径估计值dis[v]可以更小,且 v 点不在当前的队列中,就将 v 点放入队尾。这样不断从队列中取出顶点来进行松弛操作,直至队列空为止。
【检测负权回路】
方法:如果某个点进入队列的次数大于等于 n,则存在负权回路,其中 n 为图的顶点数。
const int maxn=1001;
int mp[maxn][maxn]; //邻接矩阵
bool vis[maxn]; //标记数组
int dis[maxn]; //最短距离
int pre[maxn]; //路径
int que_num[maxn]; //入队次数
int n; //顶点数
int m; //边数
int s; //源点
const int inf=0x3f;
bool SPFA()
{
memset(vis,0,sizeof(vis));
memset(que_num,0,sizeof(que_num));
for(int i=0;i<n;i++)
{
dis[i]=inf;
pre[i]=s;
}
queue<int>Q;
Q.push(s);
dis[s]=0;
vis[s]=1;
que_num[s]++;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
vis[u]=0;
for(int v=0;v<n;v++)
{
if(mp[u][v]!=inf)
{
if(dis[u]+mp[u][v]<dis[v])
{
dis[v]=dis[u]+mp[u][v];
pre[v]=u;
if(!vis[v])
{
Q.push(v);
que_num[v]++;
if(que_num[v]>=n)
return false;
vis[v]=1;
}
}
}
}
}
return true;
}