《Data Structures and Algorithm Analysis in C》(数据结构与算法分析 C语言描述)
拓扑排序
条件:无圈。顶点v的入度定义为边(u, v)的条数。步骤:
- 找出任意没有入边(入度为零)的顶点,显示该点,删除它和它的边。
- 重复步骤1直到图为空。
复杂度O(|E| + |V|)。
void topo_sort(unordered_map<string, vector<string>> graph,
unordered_map<string, int> indegree,
unordered_map<string, int> &topo_num)
{
queue<string> head; // 入度为0的点
for(auto node : indegree)
{
if(node.second == 0)
head.push(node.first);
}
int cnt = 0;
while(!head.empty())
{
string h = head.front();
head.pop();
topo_num[h] = ++cnt; // 顺序
for(auto adj : graph[h])
{
indegree[adj]--;
if(indegree[adj] == 0)
head.push(adj);
}
}
}
int main(int argc, const char** argv) {
unordered_map<string, vector<string>> graph; // 邻接表 O(|E| + |V|)
vector<string> v1 = {"v2", "v3", "v4"};
vector<string> v2 = {"v4", "v5"};
vector<string> v3 = {"v6"};
vector<string> v4 = {"v3", "v6", "v7"};
vector<string> v5 = {"v4", "v7"};
vector<string> v7 = {"v6"};
graph["v1"] = v1;
graph["v2"] = v2;
graph["v3"] = v3;
graph["v4"] = v4;
graph["v5"] = v5;
graph["v6"] = vector<string>();
graph["v7"] = v7;
unordered_map<string, int> indegree;
for(auto node : graph)
{
indegree[node.first] = 0;
}
for(auto node : graph)
{
for(auto adj : node.second)
{
indegree[adj]++;
}
}
// for(auto node : indegree)
// {
// cout << node.first << ":" << node.second << endl;
// }
unordered_map<string, int> topo_num;
topo_sort(graph, indegree, topo_num);
for(auto node : topo_num)
{
cout << node.first << "'s order: " << node.second << endl;
}
return 0;
}
Tarjan算法求割点割边
删除割点,图不再连通。
割点:C、D;割边:CG。
vector<vector<int>> Graph; // 邻接表
int times = 1; // 时间戳
vector<int> num; // 节点遍历顺序号(时间戳)
vector<int> low; // 节点不经过其父亲能到达最小时间戳
vector<int> parent;
void dfs(int v)
{
num[v] = low[v] = times++; // rule1:初始值
int children = 0;
for(int w: Graph[v])
{
children++;
if(!num[w]) // w未访问
{
parent[w] = v;
dfs(w);
if(parent[v] != -1 && low[w] >= num[v]) // case1:非root且有child
{
cout << v << "是割点" << endl;
}
else if(parent[v] == -1 && children >= 2) // case2:root且child>=2
{
cout << v << "是割点" << endl;
}
if(low[w] > num[v])
{
cout << v << "->" << w << "是割边" << endl;
}
low[v] = min(low[v], low[w]); // rule3:所有边(v,w)中最小的low[w]
}
else if(w != parent[v]) // 背向边
{
low[v] = min(low[v], num[w]); // rule2:所有背向边(v,w)中最小的num[w]
}
}
}
int main(int argc, const char** argv) {
vector<int> v0 = {};
vector<int> v1 = {2, 4};
vector<int> v2 = {1, 3};
vector<int> v3 = {2, 4, 7};
vector<int> v4 = {1, 3, 5, 6};
vector<int> v5 = {4, 6};
vector<int> v6 = {4, 5};
vector<int> v7 = {3};
Graph.push_back(v0);
Graph.push_back(v1);
Graph.push_back(v2);
Graph.push_back(v3);
Graph.push_back(v4);
Graph.push_back(v5);
Graph.push_back(v6);
Graph.push_back(v7);
num = vector<int>(Graph.size(), 0);
low = vector<int>(Graph.size(), 0);
parent = vector<int>(Graph.size(), -1);
dfs(1);
return 0;
}