LintCode 图是否是树 并查集

题目: 原题地址

给出 n 个节点,标号分别从 0 到 n - 1 并且给出一个 无向 边的列表 (给出每条边的两个顶点), 写一个函数去判断这张`无向`图是否是一棵树

 注意事项

你可以假设我们不会给出重复的边在边的列表当中. 无向边 [0, 1]和 [1, 0] 是同一条边, 因此他们不会同时出现在我们给你的边的列表当中

样例

给出n = 5 并且 edges = [[0, 1], [0, 2], [0, 3], [1, 4]], 返回 true.

给出n = 5 并且 edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], 返回 false.

 

刚开始看到题目时,想用并查集做,然后看到标签,它写是DFS。

但我觉得这种类型的题目,并查集会好很多,于是就有了下面的代码。

思路:

构建多个单向的树,用一个father数组来维护他们各各节点的关系。如果出现冲突(如A->B,C->B.这里B的父亲有两个,但由于题目中的关系是无向的,这种关系其实是A-B,C-B,也就是说可以看成A->B->C),因此先用一个list,将这些冲突的边记录下来,然后在将他们变换成无冲突的关系。

之后再通过并查集查找所有节点是否已联通,即是否会有孤立的节点。

若全部节点都联通了,而且不会有成环的情况,那就是一颗树了返回true。否则就不是,返回false

具体注释写在代码里了:

public boolean validTree(int n, int[][] edges) {
        int[] tem = new int[n];//用于记录并查集过程中,走过的节点,避免重复查
        int[] father = new int[n];//用于维护节点的父关系
        for (int i = 0; i < n; i++) {
            father[i] = i;//初始化,默认为自己,即没有父节点
        }
        List<int[]> list = new ArrayList<int[]>();//用于存储那些会冲突的节点
        for (int i = 0; i < edges.length; i++) {
            int f = edges[i][0];//默认以前一个位置为父关系节点
            int s = edges[i][1];//默认以后一个位置为子关系节点
            if (father[s] == s) {
                father[s] = f;//维护关系
            } else {
                list.add(edges[i]);//冲突后,即子节点已经有父节点了,将这个关系先存起来
            }
        }
        for (int[] e : list) {//遍历之前存起来的冲突,然后依依解决它
            int f = e[0];//父节点
            int s = e[1];//子节点

            int sf = find(father, s,tem);//查找子节点的最高父节点
            int ff = find(father, f,tem);//查找父节点的最高父节点
            if (sf == -1 || ff == -1)//如果出现==-1,则表示并查集中出现了环,给的关系为图。返回false
                return false;
            if (sf == ff)//如果出现两节点的最高父节点一样,则表是,该冲突关系最终会成环,无法解决冲突,给的关系为图,返回false
                return false;
            else
                father[ff] = sf;//解决冲突,将他们连起来
        }

        int find = find(father, 0, tem);//找第一个点的最高父节点

        for (int i = 1; i < n; i++) {
            if (find != find(father, i, tem)) {//依次与第一个节点的最高父节点比较,看是否会出现孤立的节点,出现的那么就构不成树,返回false
                return false;
            }
        }
        return true;
    }

    /**
     * 并查集
     * @param father
     * @param f
     * @param tem
     * @return
     */
    private int find(int[] father, int f, int[] tem) {
        Arrays.fill(tem, 0);//将标记清空
        /**
         * 找到f节点的最高父节点
         */
        int j = f;
        while (j != father[j]) {
            if (tem[j] != 0)
                return -1;//表示这个过程中,成环了
            tem[j] = 1;
            j = father[j];
        }
        
        /**
         * 路径压缩,加快下一次查找时的效率
         */
        int i = f, k;
        while (i != j) {
            k = father[i];
            father[i] = j;
            i = k;
        }
        
        return j;//返回找到的最高节点
    }

最后附上一张AC图~

 

posted @ 2017-08-24 11:09  夏天的冬天  阅读(465)  评论(0编辑  收藏  举报