冗余连接 II(并查集的练习)
在本问题中,有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点。
输入一个有向图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。 每一个边 的元素是一对 [u, v],用以表示有向图中连接顶点 u 和顶点 v 的边,其中 u 是 v 的一个父节点。
返回一条能删除的边,使得剩下的图是有N个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。
struct UnionFind{ vector<int> ancestor; UnionFind(int n){ ancestor.resize(n); for(int i=0;i<n;++i){ ancestor[i] = i; } } int find(int index){ //经过该find的操作,具有相同父节点的节点的parent的值都被最终标记为根节点 return index == ancestor[index]? index : ancestor[index] = find(ancestor[index]); } void merge(int u,int v){ ancestor[find(u)] = find(v); } }; class Solution { public: vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) { //由于是树多一条边,所以是n个节点n条边 int nodesCount = edges.size(); UnionFind uf = UnionFind(nodesCount + 1); vector<int> parent(nodesCount+1); for(int i =1;i<= nodesCount;++i){ parent[i] = i; } int conflict = -1; int cycle = -1; for(int i =0;i< nodesCount;++i){ auto edge = edges[i]; int node1 = edge[0],node2 = edge[1];//取出一条边的两个点 if(parent[node2]!=node2){//说明有两个父节点存在,导致了相应的冲突 conflict = i; } else{ parent[node2] = node1; if(uf.find(node1) == uf.find(node2)){ cycle = i;//两个点的祖先相同,且由于两个点之间存在相应的连线,说明存在相应的环 } else{ uf.merge(node1,node2);//将该条边插入并查集中去 } } } if(conflict<0){//没有找到存在两个父节点的冲突的边,说明导致不是树的的边是那条导致回路的边 auto redundant = vector<int> {edges[cycle][0],edges[cycle][1]};//直接申请vector,并将相应的元素放进去 return redundant; }else{ auto conflictEdge = edges[conflict]; if(cycle>=0){//冲突,同时存在导致回路的边 auto redundant = vector<int> {parent[conflictEdge[1]],conflictEdge[1]};//删除指向回路的那条边 return redundant; } else{ auto redundant = vector<int> {conflictEdge[0],conflictEdge[1]};//否则的话删除导致出现两个父节点的边 return redundant; } } } };
注意:分清可能的情况 1.加的边指向根节点,导致回路 2.指向其他节点,可能有回路也可能没回路 ,这时候存在一个节点有两个父亲节点,因此需要一个数组记录相应的父亲节点,然后在遍历的时候发现导致出现两个父亲节点的边3.处理的时候,先注意看是否有两个父亲节点,导致相应的冲突,如果没有两个父亲节点,观察是否导致相应的回路,其中观察回路的操作并不是通过所谓的深度搜索实现的,而是借助并查集,这样时间复杂度可以大幅度下降。4.遍历,记录完之后进入到了相应的处理过程,先观察是否是冲突导致的(两个父节点),如果没用冲突,说明一定是由于回路产生的,所以直接返回造成回路的点即可,否则如果有冲突的话,需要先判断是否具有回路,如果有回路的话,去除导致回路的那条边,否则直接删除冲突的这条边即可。