✿-40例题 并查集、DFS--朋友圈-547
一、题目
二、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);
}
}