有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c
    直接相连,那么城市 a 与城市 c 间接相连。
    省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
    给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相
    连,而 isConnected[i][j] = 0 表示二者不直接相连。
    返回矩阵中 省份 的数量。
    亲戚问题、朋友圈问题

    解法一:深度优先
    获取一个城市,通过递归找到离该城市最远的城市,标记为已访问,然后逐个向内进行标记

    1. public int findCircleNum(int[][] isConnected) {
    2. int provinces = isConnected.length;
    3. boolean[] visited = new boolean[provinces];
    4. int circles = 0;
    5. for (int i = 0; i < provinces; i++) {
    6. if (!visited[i]) {
    7. dfs(isConnected, visited, provinces, i);
    8. circles++;
    9. }
    10. }
    11. return circles;
    12. }
    13. public void dfs(int[][] isConnected, boolean[] visited, int provinces, int i) {
    14. for (int j = 0; j < provinces; j++) {
    15. if ((isConnected[i][j] == 1) && !visited[j]) {
    16. visited[j] = true;
    17. dfs(isConnected, visited, provinces, j);
    18. }
    19. }
    20. }

    解法二:广度优先
    获取一个城市,先标记与该城市直连的城市(最近的),然后逐步向外扩散寻找

    1. public int bfs(int[][] isConnected) {
    2. int provinces = isConnected.length;
    3. boolean[] visited = new boolean[provinces];
    4. int circles = 0;
    5. Queue<Integer> queue = new LinkedList<Integer>();
    6. for (int i = 0; i < provinces; i++) {
    7. if (!visited[i]) {
    8. queue.offer(i);
    9. while (!queue.isEmpty()) {
    10. int j = queue.poll();
    11. visited[j] = true;
    12. for (int k = 0; k < provinces; k++) {
    13. if ((isConnected[j][k] == 1) && !visited[k]) {
    14. queue.offer(k);
    15. }
    16. }
    17. }
    18. circles++;
    19. }
    20. }
    21. return circles;
    22. }

    解法三:并查集
    将每个城市看成一个节点,如果两个城市相连,则建立树关系,选出其中一个为head,
    如果两个树中的节点也相连,则将其中一个head设置为另一个树的head
    两个方法 :一个寻找head节点,一个合并树

    1. static int mergeFind(int[][] isConnected) {
    2. int provinces = isConnected.length;
    3. int[] head = new int[provinces];
    4. int[] level = new int[provinces];
    5. for (int i = 0; i < provinces; i++) {
    6. head[i] = i;
    7. level[i] = 1;
    8. }
    9. for (int i = 0; i < provinces; i++) {
    10. for (int j = i + 1; j < provinces; j++) {
    11. if (isConnected[i][j] == 1) {
    12. merge(i, j, head, level);
    13. }
    14. }
    15. }
    16. int count = 0;
    17. //找出所有的head
    18. for (int i = 0; i < provinces; i++) {
    19. if (head[i] == i) {
    20. count++;
    21. }
    22. }
    23. return count;
    24. }
    25. //查找head节点
    26. static int find(int x, int[] arr) {
    27. if (arr[x] == x) {
    28. return x;
    29. }
    30. elsearr[x] = find(arr[x], arr);
    31. //路径压缩,每一个节点直接能找到head
    32. return arr[x];
    33. }
    34. static void merge(int x, int y, int[] arr, int[] level) {
    35. int i = find(x, arr);
    36. int j = find(y, arr);
    37. //深度比较短的树的head往深度大的树上挂,使合并后的深度尽量小
    38. if (i == j) {
    39. return;
    40. }
    41. if (level[i] <= level[j]) {
    42. arr[i] = j;
    43. } else {
    44. arr[j] = i;
    45. }
    46. //深度加1
    47. level[j]++;
    48. }