Skip to content

Commit edf81d0

Browse files
committed
一刷547
1 parent 03c5364 commit edf81d0

16 files changed

+311
-69
lines changed

Diff for: README.adoc

+8-8
Original file line numberDiff line numberDiff line change
@@ -3854,14 +3854,14 @@ TIP: **公众号的微信号是: `jikerizhi`**。__因为众所周知的原因
38543854
//|{doc_base_url}/0546-remove-boxes.adoc[题解]
38553855
//|Hard
38563856
//|
3857-
//
3858-
//|{counter:codes}
3859-
//|{leetcode_base_url}/friend-circles/[547. Friend Circles^]
3860-
//|{source_base_url}/_0547_FriendCircles.java[Java]
3861-
//|{doc_base_url}/0547-friend-circles.adoc[题解]
3862-
//|Medium
3863-
//|
3864-
//
3857+
3858+
|{counter:codes}
3859+
|{leetcode_base_url}/number-of-provinces/[547. 省份数量]
3860+
|{source_base_url}/_0547_NumberOfProvinces.java[Java]
3861+
|{doc_base_url}/0547-number-of-provinces.adoc[题解]
3862+
|Medium
3863+
|
3864+
38653865
//|{counter:codes}
38663866
//|{leetcode_base_url}/split-array-with-equal-sum/[548. Split Array with Equal Sum^]
38673867
//|{source_base_url}/_0548_SplitArrayWithEqualSum.java[Java]

Diff for: docs/.asciidoctorconfig

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
:diagram_attr: format=svg,align="center",width=95%
1212

1313
:sourcedir: {asciidoctorconfigdir}/../src/main/java/com/diguage/algo/leetcode
14+
:labdir: {asciidoctorconfigdir}/../src/test/java/com/diguage/labs
1415
:java_src_attr: source%nowrap,java,indent=0,linenums,subs="attributes,verbatim,quotes"
1516

1617
:doc_base_url: {asciidoctorconfigdir}/../docs

Diff for: docs/0000-18-union-find-set.adoc

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
[#0000-18-union-find-set]
2-
= Union Find Set 查并集
2+
= Union Find Set 并查集
3+
4+
并查集算法,英文是 Union-Find,是解决动态连通性(Dynamic Conectivity)问题的一种算法。动态连通性是计算机图论中的一种数据结构,动态维护图结构中相连信息。简单的说就是,图中各个节点之间是否相连、如何将两个节点连接,连接后还剩多少个连通分量。
5+
6+
动态连通性其实可以抽象成给一幅图连线。假设用一个数组表示一堆节点,每个节点都是一个连通分量。初始化视图如下:
7+
8+
image::images/union-find-1.png[title="并查集初始化",alt="并查集初始化",{image_attr}]
9+
10+
并查集的一个重要操作是 `union(a, b)`,就是将节点 `a` 和节点 `b` 建立连接。如图所示:
11+
12+
image::images/union-find-2.png[title="并查集合并",alt="并查集合并",{image_attr}]
13+
14+
`union(a, b)` 还可以将已经建立的两个“子网”进行连接:
15+
16+
image::images/union-find-3.png[title="并查集再合并",alt="并查集再合并",{image_attr}]
17+
18+
并查集除了 `union`,还有一个重要操作是 `connnected(a, b)`。判断方法也很简单,从节点 `a``b` 开始,向上查找,直到两个节点的根节点,判断两个根节点是否相等即可判断两个节点是否已经连接。为了加快这个判断速度,可以对其进行“路径压缩”,直白点说,就是将所有树的节点,都直接指向根节点,这样只需要一步即可到达根节点。路径压缩如图所示:
19+
20+
image::images/union-find-4.png[title="并查集路径压缩",alt="并查集路径压缩",{image_attr}]
21+
22+
简单代码实现如下:
23+
24+
[{java_src_attr}]
25+
----
26+
include::{labdir}/UnionFind.java[]
27+
----
328

429
== 经典题目
530

@@ -94,3 +119,5 @@
94119
== 参考资料
95120

96121
. https://leetcode.cn/problems/redundant-connection/solutions/372045/yi-wen-zhang-wo-bing-cha-ji-suan-fa-by-a-fei-8/[684. 冗余连接 - 一文掌握并查集算法^]
122+
. https://blog.csdn.net/qq_57469718/article/details/125416286[并查集(Union-Find) (图文详解)^]
123+
. https://www.cnblogs.com/gczr/p/12077934.html[Union-Find 并查集算法^]

Diff for: docs/0547-friend-circles.adoc

-57
This file was deleted.

Diff for: docs/0547-number-of-provinces.adoc

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
[#0547-number-of-provinces]
2+
= 547. 省份数量
3+
4+
https://leetcode.cn/problems/number-of-provinces/[LeetCode - 547. 省份数量 ^]
5+
6+
`n` 个城市,其中一些彼此相连,另一些没有相连。如果城市 `a` 与城市 `b` 直接相连,且城市 `b` 与城市 `c` 直接相连,那么城市 `a` 与城市 `c` 间接相连。
7+
8+
*省份* 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
9+
10+
给你一个 `n x n` 的矩阵 `isConnected` ,其中 `isConnected[i][j] = 1` 表示第 `i` 个城市和第 `j` 个城市直接相连,而 `isConnected[i][j] = 0` 表示二者不直接相连。
11+
12+
返回矩阵中 *省份* 的数量。
13+
14+
*示例 1:*
15+
16+
image:images/0547-01.jpg[{image_attr}]
17+
18+
....
19+
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
20+
输出:2
21+
....
22+
23+
*示例 2:*
24+
25+
image:images/0547-02.jpg[{image_attr}]
26+
27+
....
28+
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
29+
输出:3
30+
....
31+
32+
*提示:*
33+
34+
* `+1 <= n <= 200+`
35+
* `n == isConnected.length`
36+
* `n == isConnected[i].length`
37+
* `isConnected[i][j]``1``0`
38+
* `isConnected[i][i] == 1`
39+
* `isConnected[i][j] == isConnected[j][i]`
40+
41+
42+
== 思路分析
43+
44+
这就是一道典型的并查集题目,所谓的“返回省份数量”就是求解连通分量。这道题的连通性是通过一个矩阵表示的,所以,首先需要将这个矩阵转换成一个 xref:0000-18-union-find-set.adoc[Union Find Set 并查集] 中讲解的数组。由于 `(i, j)` 和 `(j, i)` 表示的含义一样,所以只需要扫描矩阵的右上部分或者左下部分即可。代码如下:
45+
46+
[[src-0547]]
47+
[tabs]
48+
====
49+
一刷::
50+
+
51+
--
52+
[{java_src_attr}]
53+
----
54+
include::{sourcedir}/_0547_NumberOfProvinces.java[tag=answer]
55+
----
56+
--
57+
58+
// 二刷::
59+
// +
60+
// --
61+
// [{java_src_attr}]
62+
// ----
63+
// include::{sourcedir}/_0547_NumberOfProvinces_2.java[tag=answer]
64+
// ----
65+
// --
66+
====
67+
68+
69+
== 参考资料
70+
71+

Diff for: docs/images/0547-01.jpg

5.03 KB
Loading

Diff for: docs/images/0547-02.jpg

4.63 KB
Loading

Diff for: docs/images/union-find-1.png

87.5 KB
Loading

Diff for: docs/images/union-find-2.png

105 KB
Loading

Diff for: docs/images/union-find-3.png

96.1 KB
Loading

Diff for: docs/images/union-find-4.png

96.7 KB
Loading

Diff for: docs/index.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ include::0543-diameter-of-binary-tree.adoc[leveloffset=+1]
11701170

11711171
// include::0546-remove-boxes.adoc[leveloffset=+1]
11721172

1173-
// include::0547-friend-circles.adoc[leveloffset=+1]
1173+
include::0547-number-of-provinces.adoc[leveloffset=+1]
11741174

11751175
// include::0548-split-array-with-equal-sum.adoc[leveloffset=+1]
11761176

Diff for: logbook/202503.adoc

+7-2
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,17 @@ endif::[]
123123
|{counter:codes2503}
124124
|{leetcode_base_url}/reverse-nodes-in-k-group/[25. K 个一组翻转链表^]
125125
|{doc_base_url}/0025-reverse-nodes-in-k-group.adoc[题解]
126-
|✅ 分段递归反转,在拼接
126+
|✅ 分段递归反转,再拼接
127127

128128
|{counter:codes2503}
129129
|{leetcode_base_url}/implement-trie-prefix-tree/[208. 实现 Trie (前缀树)^]
130130
|{doc_base_url}/0208-implement-trie-prefix-tree.adoc[题解]
131-
|✅ 竟然一次通过
131+
|✅ 前缀树,竟然一次通过
132+
133+
|{counter:codes2503}
134+
|{leetcode_base_url}/number-of-provinces/[547. 省份数量]
135+
|{doc_base_url}/0547-number-of-provinces.adoc[题解]
136+
|✅ 并查集,竟然一次通过
132137

133138
|===
134139

Diff for: pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@
435435
<sourceDocumentName>index.adoc</sourceDocumentName>
436436
<attributes>
437437
<sourcedir>${project.build.sourceDirectory}/com/diguage/algo/leetcode</sourcedir>
438+
<labdir>${project.build.testSourceDirectory}/com/diguage/labs</labdir>
438439
<basedir>${project.basedir}</basedir>
439440
<doctype>book</doctype>
440441
<stem>latexmath</stem>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.diguage.algo.leetcode;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class _0547_NumberOfProvinces {
7+
// tag::answer[]
8+
9+
/**
10+
* @author D瓜哥 · https://www.diguage.com
11+
* @since 2025-04-03 22:15:52
12+
*/
13+
public int findCircleNum(int[][] isConnected) {
14+
int len = isConnected.length;
15+
UnionFind un = new UnionFind(len);
16+
for (int i = 0; i < len; i++) {
17+
// 只处理矩阵右上半部分
18+
for (int j = len - 1; j > i; j--) {
19+
if (isConnected[i][j] == 1) {
20+
un.union(i, j);
21+
}
22+
}
23+
}
24+
return un.count();
25+
}
26+
27+
private static class UnionFind {
28+
/**
29+
* 连通分量
30+
*/
31+
int size;
32+
/**
33+
* 每个节点及对应的父节点
34+
*/
35+
int[] parent;
36+
37+
public UnionFind(int size) {
38+
this.size = size;
39+
parent = new int[size];
40+
for (int i = 0; i < size; i++) {
41+
parent[i] = i;
42+
}
43+
}
44+
45+
/**
46+
* 连通分量
47+
*/
48+
public int count() {
49+
return size;
50+
}
51+
52+
/**
53+
* a 和 b 建立连接
54+
*/
55+
public void union(int a, int b) {
56+
int ap = find(a);
57+
int bp = find(b);
58+
if (ap == bp) {
59+
return;
60+
}
61+
parent[ap] = bp;
62+
size--;
63+
}
64+
65+
/**
66+
* 查找节点 a 的根节点
67+
*/
68+
private int find(int a) {
69+
int ap = parent[a];
70+
if (ap != a) {
71+
List<Integer> path = new ArrayList<>();
72+
path.add(a);
73+
// 向上查找根节点
74+
while (ap != parent[ap]) {
75+
path.add(ap);
76+
ap = parent[ap];
77+
}
78+
// 路径压缩
79+
// 只有一步,无需缩短路径
80+
if (path.size() == 1) {
81+
return ap;
82+
}
83+
for (Integer idx : path) {
84+
parent[idx] = ap;
85+
}
86+
}
87+
return ap;
88+
}
89+
}
90+
// end::answer[]
91+
}

0 commit comments

Comments
 (0)