图论基础:最短路与最小生成树
一、邻接矩阵和邻接表
邻接矩阵:二维数组
邻接表:模拟链表
二、单源最短路径:dijkstra
步骤:
- dis[i]=+oo,dis[s]=0
- 找到未标记的dis最小节点并标记
- 扫描它的出边更新其他节点
- 重复2,3,用n-1次标记所有节点
代码
void dijkstra()
{
for (int i=0;i<=n;i++) dis[i]=inf;
dis[s]=0;
for (int i=1;i<n;i++)
{
int x=0;
for (int j=1;j<=n;j++)
{
if (used[j]==0&&dis[j]<dis[x]) x=j;
}
used[x]=1;
for (int j=h[x];j;j=nxt[j])
{
dis[t[j]]=min(dis[t[j]],dis[x]+v[j]);
}
}
}
时间复杂度: O ( n m ) O(nm) O(nm)。
三、优先队列优化dij
每次从堆中找最小值
步骤:
- dis[i]=+oo,dis[s]=0
- 从堆中取出未标记的dis节点并标记(若已标记就扔掉)
- 扫描它的出边更新其他节点
- 重复2,3,在队列空前标记所有节点
代码
priority_queue<pair<int,int> >q;
void dijkstra()
{
for (int i=0;i<=n;i++) dis[i]=inf;
dis[s]=0;
q.push(make_pair(0,s));
while (q.size())
{
int x=q.top().second;
q.pop();
if (used[x]) continue;
used[x]=1;
for (int j=h[x];j;j=nxt[j])
{
if (dis[t[j]]>dis[x]+v[j])
{
dis[t[j]]=dis[x]+v[j];
q.push(make_pair(-dis[t[j]],t[j]));
}
}
}
}
时间复杂度 O ( m log n ) O(m\log n) O(mlogn)。
四、spfa
如果队列不为空:
- 取出第一个点
- 沿它寻找更优的点:更新,放入队列中
void spfa()
{
for (int i=1;i<=n;i++) dis[i]=inf;
int u,w;
q.push(s);
dis[s]=0;
vis[s]=1;
while (!q.empty())
{
u=q.front();
q.pop();
vis[u]=0;
for (int i=h[u];i!=0;i=nxt[i])
{
w=t[i];
if (dis[w]>dis[u]+v[i])
{
dis[w]=dis[u]+v[i];
if (!vis[w])
{
vis[w]=1;
q.push(w);
}
}
}
}
}
时间复杂度 O ( V E ) O(VE) O(VE)。
优点是可以解决负数的边(上面两个不行)。缺点容易退化成 O ( n m ) O(nm) O(nm)。
五、floyd
多源最短路。用 d [ k ] [ i ] [ j ] d[k][i][j] d[k][i][j]表示经过编号不超过超过 k k k的点,从 i i i到 j j j的最短路。 f [ k ] [ i ] [ j ] = m i n ( f [ k − 1 , i , j ] , m i n [ k − 1 , i , k ] + m i n [ k − 1 , k , j ] ) f[k][i][j]=min(f[k-1,i,j],min[k-1,i,k]+min[k-1,k,j]) f[k][i][j]=min(f[k−1,i,j],min[k−1,i,k]+min[k−1,k,j])。把第一维舍去即可。
缺点:时空消耗大,不能处理负环。
五、最短路的各种拓展
- 分层图最短路
- 传递闭包(Floyd)
- 最小环(Floyd无向图,堆优dij有向图)