图论基础:最短路与最小生成树
一、邻接矩阵和邻接表
邻接矩阵:二维数组
邻接表:模拟链表
二、单源最短路径: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(nm)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(mlogn)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(VE)O(VE)O(VE)。
优点是可以解决负数的边(上面两个不行)。缺点容易退化成O(nm)O(nm)O(nm)。
五、floyd
多源最短路。用d[k][i][j]d[k][i][j]d[k][i][j]表示经过编号不超过超过kkk的点,从iii到jjj的最短路。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])f[k][i][j]=min(f[k−1,i,j],min[k−1,i,k]+min[k−1,k,j])。把第一维舍去即可。
缺点:时空消耗大,不能处理负环。
五、最短路的各种拓展
- 分层图最短路
- 传递闭包(Floyd)
- 最小环(Floyd无向图,堆优dij有向图)