✿-40例题 并查集、DFS--朋友圈-547

一、题目

java朋友圈可见不可见设计_父节点

 二、Java实现代码

1、DFS方法实现

package ziDianShuAndBingChaJi;

/**
 * @Author : ASUS and xinrong
 * @Version : 2020/12/22 & 1.0
 * DFS
 * 朋友圈-547
 * 计算有多少个朋友圈
 * 思路: M矩阵为方形的,朋友圈中各个人两两互相均为好友
 *        朋友圈的个数最多不超过M.length
 *        visited就相当于记录行与列是否又被遍历过,
 *               行和列只要有一个被遍历过,另外一个也就可以忽略了,因为[1][2]和[2][1]所表达的意思相同。
 * 时间复杂度:O(n^2) 整个矩阵都要被遍历到
 * 空间复杂度:O(n) visited的大小 =》M.length
 */
public class FriendCirclesDFS {
    public int findCircleNum(int[][] M) {
        //创建visited来记录是否被浏览
        int[] visited = new int[M.length];
        //记录朋友圈的数量
        int count = 0;
        //遍历每一个一维数组
        for (int i = 0; i < M.length; i++) {
            //如果当前的行没有被浏览过
            if (visited[i] == 0) {
                //针对当前的一维数组进行DFS
                dfs(M, visited, i);
                count++;
            }
        }
        return count;
    }

    /**
     * DFS
     * @param M
     * @param visited
     * @param i 当前所要遍历的一维数组的位置
     */
    private void dfs(int[][] M, int[] visited, int i) {
        //遍历当前一维数组中的所有元素
        for (int j = 0; j < M.length; j++) {
            //如果此人在朋友圈中 并且 该列还未浏览过
            if (M[i][j] == 1 && visited[j] == 0) {
                //先将其做上标记防止重复浏览
                visited[j] =1;
                dfs(M, visited, j);
            }
        }
    }

    public static void main(String[] args) {
        FriendCirclesDFS friendCircles = new FriendCirclesDFS();
        int res = friendCircles.findCircleNum(new int[][]{{1, 1, 0}, {1, 1, 0}, {0, 0, 1}});
        System.out.println(res);
    }
}

2.并查集的方法实现

 

package ziDianShuAndBingChaJi;

import java.util.Arrays;

/**
 * @Author : ASUS and xinrong
 * @Version : 2020/12/22 & 1.0
 * 并查集
 * 朋友圈-547
 * 每棵树代表一个集合,树中的每个节点代表一个元素。
 * 思路:
 *   使用一个大小为 N 的 parent 数组进行图的遍历,
 *   每个节点都遍历所有相邻点,并让相邻点指向它,
 *   并设置成一个由 parent 节点决定的单独组。
 *   这个过程被称为 union。
 *   这样每个组都有一个唯一的 parent 节点,这些节点的父亲为 -1。
 *   对于每对新节点,找寻他们的父亲。
 *   如果父亲节点一样,那么什么都不做他们已经是一个组里。
 *   如果父亲不同,说明他们仍然需要合并。通过他们的父亲合并让他们在一个组里。
 *   最后,找到组的个数,也就是根节点的个数。这些节点的 parent 应为 -1。
 * 时间复杂度:O(n^3) 访问整个矩阵一次、并查集操作需要最坏O(n)的时间
 * 空间复杂度:O(n) parent大小为n
 */
public class FriendCirclesDisjointSet {
    public int findCircleNum(int[][] M) {
        //parent[i] 记录节点 i 的父节点;
        // 特殊的,当 i 是根节点时,parent[i] 的值为 -1 。
        // 初始时,每个节点都构成了一棵树,即每个节点都是一个根节点,
        int[] parent = new int[M.length];
        //用 -1作为值 初始化parent数组中所有的父节点
        Arrays.fill(parent, -1);
        for (int i = 0; i < M.length; i++) {
            for (int j = 0; j < M.length; j++) {
                //如果矩阵当前位置上的元素等于1 并且 不是对角线元素的话
                if (M[i][j] == 1 && i != j) {
                    //合并
                    union(parent, i, j);
                }
            }
        }
        //统计父节点的个数
        int count = 0;
        for (int i = 0; i < parent.length; i++) {
            //如果是父节点
            if (parent[i] == -1) {
                count++;
            }
        }
        return count;
    }

    /**
     * 合并父节点
     * @param parent
     * @param x 横坐标
     * @param y 纵坐标
     *  通过 find 函数找到 x,y 的根节点 xset, yset。
     */
    private void union(int[] parent, int x, int y) {
        int xset = find(parent, x);
        int yset = find(parent, y);
        // 如果两个根节点不相同,则将 x 的父节点xset设为 yset,实现合并。如果相同,则无需任何操作。
        if (xset != yset) {
            parent[xset] = yset;
        }
    }

    /**
     * 查询父节点
     * 通过 x 的父节点,父节点的父节点 ... ,一直找到根节点并返回其ID。
     * @param parent
     * @param i
     * @return
     */
    int find(int parent[], int i) {
        //如果是根节点就返回其ID
        if (parent[i] == -1) {
            return i;
        }
        //再寻找当前节点parent[i]的父节点
        return find(parent, parent[i]);
    }

    public static void main(String[] args) {
        FriendCirclesDisjointSet friendCirclesDisjointSet = new FriendCirclesDisjointSet();
        int res= friendCirclesDisjointSet.findCircleNum(new int[][]{{1, 1, 0}, {1, 1, 0}, {0, 0, 1}});
        System.out.println(res);
    }
}