当前位置:   article > 正文

547. 省份数量_点间接相连

点间接相连

地址:

力扣https://leetcode-cn.com/problems/number-of-provinces/

题目:

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2


示例 2:


输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

提示:

1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j] 为 1 或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-provinces
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:

题目有些多字,我们举个例子:

城市个数:6

城市关系值:城市之间如果直接连接,值=1,否则=0

abcdef
a111000
b110100
c101000
d010100
e000011
f000011

 直接连接或者间接连接都算作连接,这些城市就划定为一个省,依据二维数组来求得省份数量

这类题目可以变换为朋友圈数量,亲戚数量,换个名字而已,只要有关系就是一个单位

方法一、深度优先 DFS

以上图为例,起点就选用第一个城市 a,依据 a 的城市关系 [1,1,1,0,0,0] ,相连的是城市 b

继续以 b 为起点,依据 b 的城市关系 [1,1,0,1,0,0],相连的是城市 d

继续以 d 为起点,依据 d 的城市关系,没有新的城市相连,那么这条线

a->b->d 就截止,开始继续找 a 城市关系相连的下一个新城市 c

所有走过的城市我们会标注,这样寻找时不会走回头路,整个以 a 为起点的连接我们就找完了,标注也打完了,剩下没有打标注的表明不属于 a 代表的省会

继续以第二个城市 b 为起点,如此大循环,因为前序有些已结打了标注,所以,这样的遍历可能不会有标注

遍历完所有的城市,我们的标注也就全部打完了,每一次以某点开始的遍历,完成后表明一个省会的确认

  1. void dfsFindCircle(int** isConnected, int cityNum, int *markArr, int start)
  2. {
  3. int i;
  4. for(i=0; i<cityNum; i++)
  5. {
  6. //printf("in....start=%d, i=%d, markArr[%d]=%d, isConnected[%d][%d]=%d\n", start, i, i, markArr[i], start, i, isConnected[start][i]);
  7. if(markArr[i] != 1)
  8. {
  9. if(isConnected[start][i] == 1)
  10. {
  11. markArr[i] = 1;
  12. dfsFindCircle(isConnected, cityNum, markArr, i);
  13. }
  14. }
  15. }
  16. }
  17. int findCircleNum(int** isConnected, int isConnectedSize, int* isConnectedColSize){
  18. int i,j;
  19. int cityNum = *isConnectedColSize;
  20. int findNum = 0;
  21. int markLen = cityNum;
  22. int *markArr = (int *)malloc(sizeof(int) * markLen);
  23. for(i=0; i<markLen; i++)
  24. markArr[i] = 0;
  25. for(i=0; i<cityNum; i++)
  26. {
  27. //printf("out....%d, findNum=%d\n", i, findNum);
  28. if(markArr[i] != 1)
  29. {
  30. // DFS
  31. dfsFindCircle(isConnected, cityNum, markArr, i);
  32. findNum++;
  33. }
  34. }
  35. free(markArr);
  36. return findNum;
  37. }

方法二、广度优先 BFS

仔细观察城市图,其实与之前的二叉树很像,不过这里可能是多叉树

我们在二叉树的层序遍历时(二叉树遍历之层序遍历111. 二叉树的最小深度),用队列来进行。

这里找城市的连接也是一样

以城市 a 为起点,依据城市关系,与之直接连接的是城市 b,c,这是第一圈城市

分别找以 b,c 为起点的直接连接城市,找到了 d,这是第二圈城市

没有第三圈城市,后续e,f一样的逻辑

入队顺序一样:

1. a 入队

2. 队列不为空时循环处理

        2.1 出队 a

        2.2 直接连接城市 b,c,入队b,c

        2.3 出队 b,直接连接城市 d

        2.4 出队 c,没有直接连接城市

        2.5 出队 d,没有直接连接城市

一次循环完成,这个就是找到的省份,开始下一个城市为起点依据上面的逻辑

  1. int enqueue(int *queue, int len, int *rear, int cityIdx)
  2. {
  3. //printf("enqueue..rear=%d, len=%d, cityIdx=%d\n", *rear, len, cityIdx);
  4. if(*rear == len)
  5. return -1;
  6. queue[*rear] = cityIdx;
  7. (*rear)++;
  8. return 0;
  9. }
  10. int dequeue(int *queue, int len, int *front, int *rear)
  11. {
  12. if(*front == *rear)
  13. return -1;
  14. int idx = *front;
  15. if(idx == len - 1)
  16. {
  17. *front = 0;
  18. *rear = 0;
  19. }
  20. else
  21. (*front)++;
  22. //printf("dequeue..cityIdx=%d\n", queue[idx]);
  23. return queue[idx];
  24. }
  25. int isEmpty(int front, int rear)
  26. {
  27. return front == rear ? 1:0;
  28. }
  29. void checkMarkArr(int *markArr, int markLen)
  30. {
  31. int i;
  32. printf("---checkMarkArr---\n");
  33. for(i=0; i<markLen; i++)
  34. printf("markArr[%d]=%d\n", i, markArr[i]);
  35. }
  36. int findCircleNum(int** isConnected, int isConnectedSize, int* isConnectedColSize){
  37. int i,j;
  38. int cityNum = *isConnectedColSize;
  39. int findNum = 0;
  40. /* mark array */
  41. int markLen = cityNum;
  42. int *markArr = (int *)malloc(sizeof(int) * markLen);
  43. for(i=0; i<markLen; i++)
  44. markArr[i] = 0;
  45. /* queue */
  46. int front = 0;
  47. int rear = 0;
  48. int queueLen = cityNum*5;
  49. int *queueArr = (int *)malloc(sizeof(int) * queueLen);
  50. for(i=0; i<queueLen; i++)
  51. queueArr[i] = 0;
  52. for(i=0; i<cityNum; i++)
  53. {
  54. if(markArr[i] != 1)
  55. {
  56. enqueue(queueArr, queueLen, &rear, i);
  57. while(!isEmpty(front, rear))
  58. {
  59. int cityIdx = dequeue(queueArr, queueLen, &front, &rear);
  60. markArr[cityIdx] = 1;
  61. for(j=0; j<cityNum; j++)
  62. {
  63. if(isConnected[cityIdx][j] == 1 && markArr[j] != 1)
  64. enqueue(queueArr, queueLen, &rear, j);
  65. }
  66. }
  67. // printf("checkMarkArr----%d\n", i);
  68. // checkMarkArr(markArr, markLen);
  69. findNum++;
  70. }
  71. }
  72. free(markArr);
  73. free(queueArr);
  74. return findNum;
  75. }

注意:C语言自己造轮子就没有其他高级语言那么爽,这里队列的长度不一定是城市的数量

可能存在闭环的省会样式,这样某个城市对其直接连接城市入队,其他城市可能对同样的城市入队,队列长度就不够了。

以力扣的批量测试,2倍队列长度都不够,干脆直接上5倍,过了也没有细研了

方法三、并查集

城市的关联如同一棵树,多叉树

如果一个城市和一个城市是直接相连的,那么可以认定其中一个为树的根节点

再来一个城市

        如果直接相连根节点,那么也算作根节点的子节点

        如果与根节点的某一个子节点相连,那么与根节点就是间接相连,自然把其归入该树

        或者这个城市本来就是另一棵树的节点,那么就是两棵树的合并

这样的逻辑关系遍历所有城市,构建出来的树的集合,就是省份的个数

我们需要构建一个表,用于表明每个节点的直接根节点:

Idx 表示城市节点

Val 表示城市节点的直接根节点

例如:城市 4 的直接根节点是 3,3 的直接跟节点是 2, 2的根节点是自己

Idx01234
Val01223

我们还需要构建一个表,用于表明层级,因为存在合并树的情况,为了加快寻找最终根节点的速度,我们需要把 小一点的树 合并到 大一点的树

初始值都是 1,当合并同level时,选定为根节点的值++

 有新节点(树),比较level值,小值需要并入大树

 示例:

有 0 ~ 14 共 15 个城市,关系图如下

  1. int isConnected[][]=
  2. {
  3. // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  4. {1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, // 0
  5. {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 1
  6. {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 2
  7. {0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // 3
  8. {0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, // 4
  9. {0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, // 5
  10. {0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0}, // 6
  11. {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, // 7
  12. {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0}, // 8
  13. {0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1}, // 9
  14. {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0}, // 10
  15. {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, // 11
  16. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, // 12
  17. {0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0}, // 13
  18. {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1} // 14
  19. };

手动画出关系图:

如果 head 相同不做动作,图示也不进行说明

 节点 0:连接 1,7

 节点 2:孤立

节点 3:连接 5,6

节点 4:连接 9,10

节点 5:连接 10

        5 这颗树与 9这棵树产生了联系,因为树大小一样,合并后level值是要增加的

 节点 6:连接 8,13

        6 属于 9 这棵树,所以 8,13须合入 9 这棵树

 节点 7:连接 8

        7 属于 1 这棵树,小于 9 这棵树,所以须合入 1 到 9

 节点 9:连接 11, 14

        合入 11,14

 节点 12:孤立

 最后生成的就是 2,9,12 三棵树,对应的构建关系:

Head:

                        初始值:

Idx01234567891011121314
Val01234567891011121314

                        最终值:

Idx01234567891011121314
Val1925999199991299


level:

                        初始值:

Idx01234567891011121314
Val111111111111111

                        最终值:

Idx01234567891011121314
Val221223111311111

通过遍历 head数组,如果 Idx = Val,那么就表明这是一个树的根节点,否则就是某个树的子节点

所以只需要找到根节点个个数即可

代码实现:

  1. int findHead(int cityIdx, int *headArr)
  2. {
  3. int idx;
  4. if(headArr[cityIdx] == cityIdx)
  5. return cityIdx;
  6. idx = findHead(headArr[cityIdx], headArr);
  7. return idx;
  8. }
  9. void updateHead(int cityOne, int cityTwo, int *headArr, int *levelArr)
  10. {
  11. int i = findHead(cityOne, headArr);
  12. int j = findHead(cityTwo, headArr);
  13. if(i == j)
  14. return;
  15. if(levelArr[i] >= levelArr[j])
  16. headArr[j] = i;
  17. else
  18. headArr[i] = j;
  19. if(levelArr[i] == levelArr[j])
  20. {
  21. levelArr[i]++;
  22. levelArr[j]++;
  23. }
  24. }
  25. int findCircleNum(int** isConnected, int isConnectedSize, int* isConnectedColSize){
  26. int i,j;
  27. int cityNum = *isConnectedColSize;
  28. int findNum = 0;
  29. int headArrLen = cityNum;
  30. int *headArr = (int *)malloc(sizeof(int) * headArrLen);
  31. int levelArrLen = cityNum;
  32. int *levelArr = (int *)malloc(sizeof(int) * levelArrLen);
  33. for(i=0; i<cityNum; i++)
  34. {
  35. headArr[i] = i;
  36. levelArr[i] = 1;
  37. }
  38. for(i=0; i<cityNum; i++)
  39. {
  40. for(j=i+1; j<cityNum; j++)
  41. {
  42. if(isConnected[i][j] == 1)
  43. updateHead(i, j, headArr, levelArr);
  44. }
  45. }
  46. for(i=0; i<cityNum; i++)
  47. {
  48. if(headArr[i] == i)
  49. findNum++;
  50. }
  51. free(headArr);
  52. free(levelArr);
  53. return findNum;
  54. }

查看更多刷题笔记

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/531706
推荐阅读
相关标签
  

闽ICP备14008679号