冗余连接 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.遍历,记录完之后进入到了相应的处理过程,先观察是否是冲突导致的(两个父节点),如果没用冲突,说明一定是由于回路产生的,所以直接返回造成回路的点即可,否则如果有冲突的话,需要先判断是否具有回路,如果有回路的话,去除导致回路的那条边,否则直接删除冲突的这条边即可。

posted @ 2020-09-17 18:24  zmachine  阅读(167)  评论(0编辑  收藏  举报