图论
单源最短路径
边权全部为正的时候,Dijkstra算法最优秀,还可以优先队列优化。
Dijkstra算法朴素版需要循环枚举出来当前的最小值(作为优化的起点) 所以可以用大顶堆来优化
设置集合S存放已被访问的顶点,然后执行①②
- 每次从集合(未被攻占)中选择与起点最短距离最小的点(记为U),访问并加入集合(被攻占)
- 令顶点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替代起点。
- 每次从集合V-S中选择与集合AS最近的一个顶点(u) , 访问u并且将其加入S , sum累加
- 令集合u作为集合S与集合V-S连接的接口,优化从u能到达的未访问的顶点v与集合S的最短距离
- Dijkstra中d[]的含义是
起点s
到达顶点Vi
的最短距离 - Prim中d[]的含义是顶点
Vi
到集合
的最短距离
Kruskal算法
边贪心策略 并查集+结构体排序
- 对所有的边权进行从大到小排序
- 按边权从小到达测试所有边,如果两边不在一个连通块中,则将次边加入最小生成树中,否则舍弃
- 重复2,直到最小生成树中的
边数 == n-1
的时候结束,如果结束的时候边数小于n-1
则该图不联通
【适用情况】: 稠密图(边多)使用Prim算法 稀疏图(边少)使用Kruskal