[算法笔记] 图论总结
本文组织结构:
- 并查集
- Kruskal
- Dijkstra
- DFS
- BFS
并查集 (Disjoint Set)
思想比较简单,一个无环的连同图可以看作是一棵树,任意选定一个节点为根,并查集可找出树中每个节点的最远的根(或者说是“最早的祖先”)。
#include <iostream>
#include <cstring>
#include <map>
#include <vector>
#define VMAX 100
using namespace std;
int uset[VMAX + 1];
int findRoot(int x)
{
if (uset[x] == -1)
return x;
else
return uset[x] = findRoot(uset[x]);
}
int main()
{
memset(uset, -1, sizeof(uset));
int v, e;
int a, b;
cin >> v >> e;
while (e--)
{
cin >> a >> b;
a = findRoot(a);
b = findRoot(b);
if (a - b)
uset[a] = b;
}
//连通分量个数就是 -1 的个数
for (int i = 1; i <= v; i++)
cout << uset[i] << ' ';
}
/*
输入: 点数, 边数, 顶点从 0 开始
6 3
1 2
3 4
5 6
*/
Kruskal
最小生成树求解算法。
对所有的边升序排列,每次选出一个最小边 <u,v,cost>
,如果 u
和 v
不是连通的(借助并查集判断),说明该边一定属于 MST
。
#include <map>
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <cstring>
#define VMAX 100
#define EMAX 100
using namespace std;
struct Node
{
int u, v;
int cost = 0;
Node(int a = -1, int b = -1, int c = 0)
{
u = a;
v = b;
cost = c;
}
//当权值相同时也允许插入set,注意set的插入cmp比较条件的使用
bool operator<(const Node &n) const
{
return cost <= n.cost;
}
};
int root[VMAX];
set<Node> graph; //要求以边为权值排序
int findRoot(int x)
{
return (root[x] == -1) ? (x) : (root[x] = findRoot(root[x]));
}
int main()
{
memset(root, -1, sizeof(root));
int v, e;
cin >> v >> e;
int a, b, c;
for (int i = 0; i < e; i++)
{
cin >> a >> b >> c;
//无向图
graph.insert(Node(a, b, c));
}
int mincost = 0;
for (auto &x : graph)
{
a = findRoot(x.u);
b = findRoot(x.v);
if (a != b)
{
root[a] = b;
mincost += x.cost;
cout << x.u << " " << x.v << ' ' << x.cost << endl;
}
}
cout << mincost << endl;
}
/*
输入样例
6 8
1 6 100
1 5 30
1 3 10
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
mincost = 75
*/
Dijkstra
借助优先队列优化的 dij
算法。
先来回顾一下最短路径的关键的点:
- 选出一个
cost
最小的点x
- 扫描所有与
x
相连的点y
- 判断
[start->...->y]
与[start->...->x->...->y]
哪一个路径最短
在原始的 Dijkstra
算法中,第一点找出最小 cost
的复杂度是 0(n)
,现在利用优先队列的自动排序,找出最小 cost
的复杂度是 0(logn)
,最终算法被优化为 O(nlogn)
。
#include <cstring>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#define VMAX 100
#define EMAX 100
using namespace std;
struct Node
{
int adjvex;
int cost;
Node(int a = -1, int c = 0) : adjvex(a), cost(c) {}
//priority queue 中 cost 小的在前面,具有更高的优先级
bool operator<(const Node &n) const
{
return cost > n.cost;
}
};
map<int, vector<Node>> m;
int dis[VMAX];
bool vis[VMAX];
int prevex[VMAX];
void path(int x)
{
if (x == -1)
return;
path(prevex[x]);
cout << x << ' ';
}
/*
使用优先队列优化,实质上利用了 priority_queue 的自动排序
每次 q.top 都是 mincost
其实用 set 也可以
*/
void dij(int start, int v)
{
memset(prevex, -1, sizeof(prevex));
memset(vis, 0, sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
dis[start] = 0;
priority_queue<Node> q;
q.push(Node(start, 0));
Node n;
while (!q.empty())
{
n = q.top();
q.pop();
int x = n.adjvex;
if (!vis[x])
{
vis[x] = true;
int len = m[x].size();
for (auto &n : m[x])
{
int y = n.adjvex;
int newcost = dis[x] + n.cost;
if (vis[y])
continue;
if (dis[y] > newcost)
{
dis[y] = newcost;
q.push(Node(y, newcost));
prevex[y] = x;
}
else if (dis[y] == newcost)
{
//多个最短路径
}
}
}
}
}
int main()
{
int v, e;
int a, b, c;
cin >> v >> e;
for (int i = 0; i < e; i++)
{
cin >> a >> b >> c;
m[a].push_back(Node(b, c));
m[b].push_back(Node(a, c));
}
dij(1, v);
for (int i = 1; i <= v; i++)
{
path(i);
cout << dis[i] << endl;
}
}
/*
Sample
6 8
1 6 100
1 5 30
1 3 10
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
*/
DFS和BFS
经典算法。
BFS
实现有 2
种方法:一种是把已经访问的放入队列;一种是类似于二叉树的层次遍历,把与当前的点 x
(已经被访问)相邻的,未被访问且不在队列中的点放入队列(是否在队列用 vis[y]=2 ?
来表示 )。
#include "leetcode.h"
#define VMAX 100
map<int, vector<int>> m;
int vis[VMAX] = {0};
void dfs(int x)
{
vis[x] = 1;
cout << x << ' ';
for (int y : m[x])
{
if (!vis[y])
dfs(y);
}
}
void bfs(int x)
{
queue<int> q;
q.push(x);
int y;
while (!q.empty())
{
y = q.front();
q.pop();
cout << y << ' ';
vis[y] = 1;
for (int z : m[y])
{
if (!vis[z] && vis[z] != 2)
q.push(z), vis[z] = 2;
}
}
}
int main()
{
int v, e;
cin >> v >> e;
int a, b;
for (int i = 0; i < e; i++)
{
cin >> a >> b;
m[a].push_back(b);
m[b].push_back(a);
}
for (int i = 1; i <= v; i++)
{
memset(vis, 0, sizeof(vis));
bfs(i);
cout << endl;
}
}
/*
Sample1
7 6
1 2
1 3
2 4
2 5
3 6
3 7
Sample2
6 8
1 3
1 5
1 6
2 3
3 4
4 5
4 6
5 6
*/