图论-最短路径算法总结

图论


单源最短路径

边权全部为正的时候,Dijkstra算法最优秀,还可以优先队列优化。

Dijkstra算法朴素版需要循环枚举出来当前的最小值(作为优化的起点) 所以可以用大顶堆来优化

设置集合S存放已被访问的顶点,然后执行①②

  1. 每次从集合(未被攻占)中选择与起点最短距离最小的点(记为U),访问并加入集合(被攻占)
  2. 令顶点U为中介点,优化最短路

邻接矩阵

int n,G[maxn][maxn]; // n 顶点数
int d[maxn]; //起点到达各点最短距离长度
bool vis[maxn] = {
    
    false}; //true为访问过 false未访问
void Dijkstra(int s){
    
    
	fill(d,d+maxn,INF);
	vis[n] = true;
	for(int i=0;i<n;i++){
    
    
		int u = -1,MIN = INF;
		for(int j=0;j<n;j++){
    
    
			if(!vis[j]&&d[j]<MIN){
    
    
				u = j;
				MIN = d[j];
			}
		}
		//找不到小于INF的,说明剩下的顶点和起点不连通
		if(u==-1)return; 
		vis[u] = true;
		for(int v=0;v<n;v++){
    
    
			if(!vis[v]&&G[u][v]!=INF && d[u]+G[u][v]<d[v]){
    
    
				d[v] = d[u]+G[u][v]; //优化d[v]
			}
		}
	}
}

code邻接表

struct node{
    
    
    int v,dis; //v为边的目标点 dis为边权
};
vector<node> adj[maxn]; //图G,adj[u]存放从顶点u出发可以达到的所有顶点
int n;
int d[maxn]; // 起点到达各点的最短路径长度
bool vis[maxn] = {
    
    false};

void Dijkstra(int s){
    
    
    fill(d,d+maxn,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]&&d[j]<MIN){
    
    
                u = j;
                MIN = d[j];
            }
        }
        if(u==-1)return;
        vis[u] = true; //标记u已访问
        for(int j=0;j<adj[u].size();j++){
    
    
            int v = adj[u][j].v; //拿出来目标点
            if(!vis[v]&&d[u]+adj[u][j].dis<d[v]){
    
    
                d[v] = d[u]+adj[u][j].dis; //优化
            }
        }
    }
}

如果我们要记录前驱;则在优化处新增pre[]数组,pre[v] = u ;代表v的前驱节点是u,再递归输出

//先递归,再输出
void dfs(int start,int v){
    
    
	if(s==v){
    
    
		cout<<s<<endl;
		return;
	}
	dfs(start,pre[v]);
	cout<<v<<endl;
}

ps:如果有多个限制属性,再新增规则就好。

边权有为负的时候 可以使用Bellman-Ford算法和SPFA算法

Bellman-Ford算法是暴力思想,由于最短路径树不超过n层,所以只需要做n-1次松弛操作
再对每条边进行计算,如果还可以松弛,则存在负环 时间复杂度o(V^3)

SPFA算法就是Bellman-Ford算法的优化
(队列记录每个点进行松弛)队列为空的时候,没有负环
一个点入队次数超过n-1次,则存在从源点可以到达的负环

全源最短路径问题

求任意两点之间的最短距离

枚举顶点 k [1 , n]
    以顶点k作为中介点,枚举所有顶点对i和j
        if( dis[i][k] + dis[k][j] < dis[i][j])
            dis[i][j] = dis[i][k]+dis[k][j];

Floyd();

最小生成树

给定图G(V,E)中求一棵树T(连接所有顶点)

Prim算法

基本思想与Dijkstra算法相似,只不过用一个新的集合S替代起点。

  1. 每次从集合V-S中选择与集合AS最近的一个顶点(u) , 访问u并且将其加入S , sum累加
  2. 令集合u作为集合S与集合V-S连接的接口,优化从u能到达的未访问的顶点v与集合S的最短距离
  • Dijkstra中d[]的含义是起点s到达顶点Vi的最短距离
  • Prim中d[]的含义是顶点Vi集合的最短距离

Kruskal算法

边贪心策略 并查集+结构体排序

  1. 对所有的边权进行从大到小排序
  2. 按边权从小到达测试所有边,如果两边不在一个连通块中,则将次边加入最小生成树中,否则舍弃
  3. 重复2,直到最小生成树中的边数 == n-1的时候结束,如果结束的时候边数小于n-1则该图不联通

【适用情况】: 稠密图(边多)使用Prim算法 稀疏图(边少)使用Kruskal

猜你喜欢

转载自blog.csdn.net/zhimeng_LQ/article/details/107325166
今日推荐