力扣685冗余连接2
[题目](685. 冗余连接 II - 力扣(LeetCode))
多出的边分为两种情况。
1.边连向根节点,根节点的入度为1
将会形成有向环
2.边连向非根节点,其中一个非根节点的入度为2
可能会形成有向环或者没有有向环

这两种情况是互斥的。
第一种情况根节点入度为1:
问题可以退化为[冗余连接1](684. 冗余连接 - 力扣(LeetCode)),用并查集判断连通性,删去最后一条已经连通的点之间的边即可。
第二种情况:
先记录入度为2的点,判断删去其中一条边是否还构成树,不够成这条边就是答案,反之另一条边才是答案。
问题的关键在于如何判断是否构成一颗树,可以用并查集来判断,并查集可以判断一种 "类环结构"(无向环) 这种结构要么是
有向环 要么是其中一个节点的入度为 2 但是并查集不能准确判断是哪一种。
例如图中的第二种有环的情况:
[1,2],[2,3],[3,1],[4,3] 依次通过并查集合并操作,最后发现了3和1已经连通了无法构成树。
第二种无环的情况:
[1,2],[1,3],[2,3]依次并查集合并操作,最后发现进行[2,3]合并时,2和3已经连通了无法构成树。
核心代码
class Solution {
public:
const static int N=1000+10;
int fa[N],rk[N];
void build(int n){
for(int i=1;i<=n;i++){
fa[i]=i;
rk[i]=0;
}
}
int find(int x){
if(x!=fa[x]){
fa[x]=find(fa[x]);
}
return fa[x];
}
void unite(int x,int y){
x=find(x);
y=find(y);
if(x==y) return ;
if(rk[x]<rk[y]){
fa[x]=y;
}else{
fa[y]=x;
if(rk[x]==rk[y]) rk[x]++;
}
}
//删除一条边后是否构成树
bool check(int n,vector<vector<int>>& edges,int delEdge){
build(n);
for(int i=0;i<n;i++){
if(i==delEdge) continue;
if(find(edges[i][0])==find(edges[i][1])) return false;
unite(edges[i][0],edges[i][1]);
}
return true;
}
vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
int n=edges.size();
build(n);
vector<int> inDegree(n+1,0);
//入度
for(int i=0;i<edges.size();i++){
inDegree[edges[i][1]]++;
}
//记录入度为2的边
vector<int> vec;
for(int i=n-1;i>=0;i--){
if(inDegree[edges[i][1]]==2){
vec.push_back(i);
}
}
//1:根节点入度为1 2:存在入度为2的节点
if(vec.size()>0){
if(check(n,edges,vec[0])){
return edges[vec[0]];
}else return edges[vec[1]];
}
// 根节点构成有向环 根节点入度为1
build(n);
for(int i=0;i<n;i++){
int u=edges[i][0],v=edges[i][1];
if(find(u)==find(v)) return edges[i];
unite(u,v);
}
return {};
}
};