108. 冗余连接
并查集。
在输入时判断两个节点是否位于一个集合,若是,则加入当前边会出现环,则当前边就是可以去除的边。
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
// c++
#include<bits/stdc++.h>
using namespace std;
void init(vector<int> &father){
for(int i=0; i<father.size(); i++) father[i] = i;
}
int find(vector<int> &father, int u){
if(u == father[u]) return u;
return father[u] = find(father, father[u]);
}
bool isSame(vector<int> &father, int u, int v){
u = find(father, u);
v = find(father, v);
return u == v;
}
void join(vector<int> &father, int u, int v){
u = find(father, u);
v = find(father, v);
if(u==v)return;
father[v] = u;
}
int main(){
int n,s,t;
cin>>n;
vector<int> father(n+1, 0);
init(father);
vector<int> result;
for(int i=0; i<n; i++){
cin>>s>>t;
// 可以直接return 0结束程序
if(isSame(father, s, t)) result = {s, t};
else join(father, s, t);
}
cout<<result[0]<<" "<<result[1];
return 0;
}
*109. 冗余连接II
并查集。上强度了。
有向图删除一条边变成有向树,有两种情况:
- 存在一个节点的入度为2,将该结点的入边删除一条即可,具体删除哪一条需要进一步判断删除后是否是树。
- 若没有入度为2的结点,则说明图中存在环,则删除环中的一条边即可。
使用并查集来查看当前的结点是否处在同一颗树中:
- 若新加入边的两个节点均已在同一棵树种,则加入该边会出现环。
- 反之,则将当前边加入树中(将u->v边中的v加入u,即入边加入出边集合)
tips: 当有多条边可删除时,删除最后出现的一条。
并查集的时间复杂度在路径压缩后处于:
O
(
l
o
g
n
)
O(logn)
O(logn) ~
O
(
1
)
O(1)
O(1) 之间
时间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
空间复杂度:
O
(
n
)
O(n)
O(n)
// c++
#include<bits/stdc++.h>
using namespace std;
void init(vector<int> &father){
for(int i=1; i<father.size(); i++) father[i] = i;
}
int find(vector<int> &father, int u){
return u == father[u] ? u : father[u] = find(father, father[u]);
}
bool isSame(vector<int> &father, int u, int v){
u = find(father, u);
v = find(father, v);
return u == v;
}
void join(vector<int> &father, int u, int v){
u = find(father, u);
v = find(father, v);
if(u == v) return ;
father[v] = u;
}
void getRemoveEdge(vector<int> &father, const vector<vector<int>> &edges){
init(father);
for(int i=0; i<edges.size(); i++){
// 成环
if(isSame(father, edges[i][0], edges[i][1])){
cout<<edges[i][0]<<" "<<edges[i][1]<<endl;
return;
}
join(father, edges[i][0], edges[i][1]);
}
}
bool isTreeAfterRemove(vector<int> &father, const vector<vector<int>> &edges, int removeEdge){
init(father);
for(int i=0; i<edges.size(); i++){
if(i == removeEdge) continue;
// 成环
if(isSame(father, edges[i][0], edges[i][1])){
return false;
}
join(father, edges[i][0], edges[i][1]);
}
return true;
}
int main(){
int n, s, t;
cin>>n;
vector<vector<int>> edges;
vector<int> father(n+1, 0);
// 统计入度
vector<int> inDegree(n+1, 0);
// 记录入度大于1(等于2)的结点
vector<int> vec;
init(father);
for(int i=0; i<n; i++){
cin>>s>>t;
edges.push_back({s, t});
inDegree[t]++;
if(inDegree[t]>1){
vec.emplace_back(i);
}
}
// 若有多条可删除的边,删除最后出现的一条
int len = vec.size();
if(len>0){
// 优先删除最后出现的边
if(isTreeAfterRemove(father, edges, vec[len-1])){
int i = vec[len-1];
cout<<edges[i][0]<<" "<<edges[i][1]<<endl;
}
else{
int i = vec[len-2];
cout<<edges[i][0]<<" "<<edges[i][1]<<endl;
}
return 0;
}
// 没有入度大于1的结点 说明有环
getRemoveEdge(father, edges);
return 0;
}