回顾了这几道题之后,写一些总结。
下面是无向图的四种情况:
无环连通图 | 有环连通图 | 无环非连通图(每一个连通分量都是无环的) | 有环非连通图(至少有一个连通分量是有环的) |
---|---|---|---|
边数 = 点数 - 1 | 边数 > 点数 - 1 | 边数 < 点数 - 1(点数 - 边数 = 连通分量数,且连通分量数 > 1) | 左边三种情况都有可能 |
从这个表格中可以看出以下两点:
- 若 边数 < 点数 - 1,则一定是非连通图。
- 若 边数 > 点数 - 1,则一定有环。
1. 求解无向图有几个连通分量
这个问题就是用并查集测试每一条边,看看conn
最后是多少(上限为V.size()-1
),因为conn
相当于原图中所有连通分量各自的生成树的边数之和(原图的无环图版本,不改变原有的连通分量数),然后答案等于V.size()-conn
for (int i = 0; i < E.size(); i++)
{
if (u(E[i].n1, E[i].n2))
conn++;
if (conn == V.size() - 1) break;
}
printf("%d\n", V.size() - conn);
2. 判断无向图有没有环
首先,若 边数 > 点数 - 1,则一定有环。
然后还是用并查集测试每一条边,只要u()
返回false
(f(n1)==f(n2)
)就说明有环。
if (E.size() > V.size() - 1 && !V.empty())
{
printf("有环\n");
return;
}
for (int i = 0; i < E.size(); i++)
{
if (!u(E[i].n1, E[i].n2))
{
printf("有环\n");
return;
}
}
printf("无环\n");
return;
3. 判定 4 种无向图
判断 有环连通图 不能用flag
,因为有可能边表的前V.size()-1
条边都conn++
了。
bool flag = false;
for (int i = 0; i < E.size(); i++)
{
if (!u(E[i].n1, E[i].n2))
flag = true;
else conn++;
if (conn == V.size() - 1) break;
}
if (conn == V.size() - 1)
{
if (E.size() == V.size() - 1)
printf("无环连通图\n");
else printf("有环连通图\n");
}
else
{
if (flag) printf("有环非连通图\n");
else printf("无环非连通图\n");
}