Skip to content

Commit 2c5797f

Browse files
committed
feat: add solutions to lc problem: No.0886
No.0886.Possible Bipartition
1 parent e41af32 commit 2c5797f

File tree

6 files changed

+376
-157
lines changed

6 files changed

+376
-157
lines changed

solution/0800-0899/0886.Possible Bipartition/README.md

+186-77
Original file line numberDiff line numberDiff line change
@@ -56,65 +56,59 @@
5656

5757
<!-- 这里可写通用的实现逻辑 -->
5858

59-
并查集。
59+
**方法一:染色法**
6060

61-
模板 1——朴素并查集:
61+
我们用两种颜色对图进行染色,如果可以完成染色,那么就说明可以将所有人分进两组。
6262

63-
```python
64-
# 初始化,p存储每个点的父节点
65-
p = list(range(n))
63+
具体的染色方法如下:
6664

67-
# 返回x的祖宗节点
68-
def find(x):
69-
if p[x] != x:
70-
# 路径压缩
71-
p[x] = find(p[x])
72-
return p[x]
65+
- 初始化所有人的颜色为 $0$,表示还没有染色。
66+
- 遍历所有人,如果当前人没有染色,那么就用颜色 $1$ 对其进行染色,然后将其所有不喜欢的人用颜色 $2$ 进行染色。如果染色过程中出现了冲突,那么就说明无法将所有人分进两组,返回 `false`
67+
- 如果所有人都染色成功,那么就说明可以将所有人分进两组,返回 `true`
7368

69+
时间复杂度 $O(n + m)$,其中 $n$, $m$ 分别是人数和不喜欢的关系数。
7470

75-
# 合并a和b所在的两个集合
76-
p[find(a)] = find(b)
77-
```
71+
**方法二:并查集**
72+
73+
并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的**合并****查询**问题。 它支持两种操作:
74+
75+
1. 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 $O(\alpha(n))$
76+
1. 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 $O(\alpha(n))$
77+
78+
其中 $\alpha$ 为阿克曼函数的反函数,其增长极其缓慢,也就是说其单次操作的平均运行时间可以认为是一个很小的常数。
79+
80+
以下是并查集的常用模板,需要熟练掌握。其中:
7881

79-
模板 2——维护 size 的并查集:
82+
- `n` 表示节点数
83+
- `p` 存储每个点的父节点,初始时每个点的父节点都是自己
84+
- `size` 只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
85+
- `find(x)` 函数用于查找 $x$ 所在集合的祖宗节点
86+
- `union(a, b)` 函数用于合并 $a$ 和 $b$ 所在的集合
8087

8188
```python
82-
# 初始化,p存储每个点的父节点,size只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
8389
p = list(range(n))
8490
size = [1] * n
8591

86-
# 返回x的祖宗节点
8792
def find(x):
8893
if p[x] != x:
8994
# 路径压缩
9095
p[x] = find(p[x])
9196
return p[x]
9297

93-
# 合并a和b所在的两个集合
94-
if find(a) != find(b):
95-
size[find(b)] += size[find(a)]
96-
p[find(a)] = find(b)
98+
99+
def union(a, b):
100+
pa, pb = find(a), find(b)
101+
if pa == pb:
102+
return
103+
p[pa] = pb
104+
size[pb] += size[pa]
97105
```
98106

99-
模板 3——维护到祖宗节点距离的并查集:
107+
对于本题,我们遍历每一个人,他与他不喜欢的人不应该在同一个集合中,如果在同一个集合中,就产生了冲突,直接返回 `false`。如果没有冲突,那么就将他所有不喜欢的人合并到同一个集合中。
100108

101-
```python
102-
# 初始化,p存储每个点的父节点,d[x]存储x到p[x]的距离
103-
p = list(range(n))
104-
d = [0] * n
109+
遍历结束,说明没有冲突,返回 `true`
105110

106-
# 返回x的祖宗节点
107-
def find(x):
108-
if p[x] != x:
109-
t = find(p[x])
110-
d[x] += d[p[x]]
111-
p[x] = t
112-
return p[x]
113-
114-
# 合并a和b所在的两个集合
115-
p[find(a)] = find(b)
116-
d[find(a)] = distance
117-
```
111+
时间复杂度 $O(n + m\times \alpha(n))$。
118112

119113
<!-- tabs:start -->
120114

@@ -125,53 +119,113 @@ d[find(a)] = distance
125119
```python
126120
class Solution:
127121
def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool:
128-
p = list(range(n))
122+
def dfs(i, c):
123+
color[i] = c
124+
for j in g[i]:
125+
if color[j] == c:
126+
return False
127+
if color[j] == 0 and not dfs(j, 3 - c):
128+
return False
129+
return True
129130

131+
g = defaultdict(list)
132+
color = [0] * n
133+
for a, b in dislikes:
134+
a, b = a - 1, b - 1
135+
g[a].append(b)
136+
g[b].append(a)
137+
return all(c or dfs(i, 1) for i, c in enumerate(color))
138+
```
139+
140+
```python
141+
class Solution:
142+
def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool:
130143
def find(x):
131144
if p[x] != x:
132145
p[x] = find(p[x])
133146
return p[x]
134147

135-
dis = defaultdict(list)
148+
g = defaultdict(list)
136149
for a, b in dislikes:
137150
a, b = a - 1, b - 1
138-
dis[a].append(b)
139-
dis[b].append(a)
140-
151+
g[a].append(b)
152+
g[b].append(a)
153+
p = list(range(n))
141154
for i in range(n):
142-
for j in dis[i]:
155+
for j in g[i]:
143156
if find(i) == find(j):
144157
return False
145-
p[find(j)] = find(dis[i][0])
158+
p[find(j)] = find(g[i][0])
146159
return True
147160
```
148161

149162
### **Java**
150163

151164
<!-- 这里可写当前语言的特殊实现逻辑 -->
152165

166+
```java
167+
class Solution {
168+
private List<Integer>[] g;
169+
private int[] color;
170+
171+
public boolean possibleBipartition(int n, int[][] dislikes) {
172+
g = new List[n];
173+
color = new int[n];
174+
for (int i = 0; i < n; ++i) {
175+
g[i] = new ArrayList<>();
176+
}
177+
for (var e : dislikes) {
178+
int a = e[0] - 1, b = e[1] - 1;
179+
g[a].add(b);
180+
g[b].add(a);
181+
}
182+
for (int i = 0; i < n; ++i) {
183+
if (color[i] == 0) {
184+
if (!dfs(i, 1)) {
185+
return false;
186+
}
187+
}
188+
}
189+
return true;
190+
}
191+
192+
private boolean dfs(int i, int c) {
193+
color[i] = c;
194+
for (int j : g[i]) {
195+
if (color[j] == c) {
196+
return false;
197+
}
198+
if (color[j] == 0 && !dfs(j, 3 - c)) {
199+
return false;
200+
}
201+
}
202+
return true;
203+
}
204+
}
205+
```
206+
153207
```java
154208
class Solution {
155209
private int[] p;
156210

157211
public boolean possibleBipartition(int n, int[][] dislikes) {
158212
p = new int[n];
159-
List<Integer>[] dis = new List[n];
213+
List<Integer>[] g = new List[n];
160214
for (int i = 0; i < n; ++i) {
161215
p[i] = i;
162-
dis[i] = new ArrayList<>();
216+
g[i] = new ArrayList<>();
163217
}
164-
for (int[] d : dislikes) {
165-
int a = d[0] - 1, b = d[1] - 1;
166-
dis[a].add(b);
167-
dis[b].add(a);
218+
for (var e : dislikes) {
219+
int a = e[0] - 1, b = e[1] - 1;
220+
g[a].add(b);
221+
g[b].add(a);
168222
}
169223
for (int i = 0; i < n; ++i) {
170-
for (int j : dis[i]) {
224+
for (int j : g[i]) {
171225
if (find(i) == find(j)) {
172226
return false;
173227
}
174-
p[find(j)] = find(dis[i].get(0));
228+
p[find(j)] = find(g[i].get(0));
175229
}
176230
}
177231
return true;
@@ -191,60 +245,115 @@ class Solution {
191245
```cpp
192246
class Solution {
193247
public:
194-
vector<int> p;
248+
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
249+
unordered_map<int, vector<int>> g;
250+
for (auto& e : dislikes) {
251+
int a = e[0] - 1, b = e[1] - 1;
252+
g[a].push_back(b);
253+
g[b].push_back(a);
254+
}
255+
vector<int> color(n);
256+
function<bool(int, int)> dfs = [&](int i, int c) -> bool {
257+
color[i] = c;
258+
for (int j : g[i]) {
259+
if (!color[j] && !dfs(j, 3 - c)) return false;
260+
if (color[j] == c) return false;
261+
}
262+
return true;
263+
};
264+
for (int i = 0; i < n; ++i) {
265+
if (!color[i] && !dfs(i, 1)) return false;
266+
}
267+
return true;
268+
}
269+
};
270+
```
195271
272+
```cpp
273+
class Solution {
274+
public:
196275
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
197-
p.resize(n);
198-
for (int i = 0; i < n; ++i) p[i] = i;
199-
unordered_map<int, vector<int>> dis;
200-
for (auto& d : dislikes) {
201-
int a = d[0] - 1, b = d[1] - 1;
202-
dis[a].push_back(b);
203-
dis[b].push_back(a);
276+
vector<int> p(n);
277+
iota(p.begin(), p.end(), 0);
278+
unordered_map<int, vector<int>> g;
279+
for (auto& e : dislikes) {
280+
int a = e[0] - 1, b = e[1] - 1;
281+
g[a].push_back(b);
282+
g[b].push_back(a);
204283
}
284+
function<int(int)> find = [&](int x) -> int {
285+
if (p[x] != x) p[x] = find(p[x]);
286+
return p[x];
287+
};
205288
for (int i = 0; i < n; ++i) {
206-
for (int j : dis[i]) {
289+
for (int j : g[i]) {
207290
if (find(i) == find(j)) return false;
208-
p[find(j)] = find(dis[i][0]);
291+
p[find(j)] = find(g[i][0]);
209292
}
210293
}
211294
return true;
212295
}
213-
214-
int find(int x) {
215-
if (p[x] != x) p[x] = find(p[x]);
216-
return p[x];
217-
}
218296
};
219297
```
220298

221299
### **Go**
222300

301+
```go
302+
func possibleBipartition(n int, dislikes [][]int) bool {
303+
g := make([][]int, n)
304+
for _, e := range dislikes {
305+
a, b := e[0]-1, e[1]-1
306+
g[a] = append(g[a], b)
307+
g[b] = append(g[b], a)
308+
}
309+
color := make([]int, n)
310+
var dfs func(int, int) bool
311+
dfs = func(i, c int) bool {
312+
color[i] = c
313+
for _, j := range g[i] {
314+
if color[j] == c {
315+
return false
316+
}
317+
if color[j] == 0 && !dfs(j, 3-c) {
318+
return false
319+
}
320+
}
321+
return true
322+
}
323+
for i, c := range color {
324+
if c == 0 && !dfs(i, 1) {
325+
return false
326+
}
327+
}
328+
return true
329+
}
330+
```
331+
223332
```go
224333
func possibleBipartition(n int, dislikes [][]int) bool {
225334
p := make([]int, n)
226-
dis := make([][]int, n)
335+
g := make([][]int, n)
227336
for i := range p {
228337
p[i] = i
229338
}
230-
var find func(x int) int
339+
for _, e := range dislikes {
340+
a, b := e[0]-1, e[1]-1
341+
g[a] = append(g[a], b)
342+
g[b] = append(g[b], a)
343+
}
344+
var find func(int) int
231345
find = func(x int) int {
232346
if p[x] != x {
233347
p[x] = find(p[x])
234348
}
235349
return p[x]
236350
}
237-
for _, d := range dislikes {
238-
a, b := d[0]-1, d[1]-1
239-
dis[a] = append(dis[a], b)
240-
dis[b] = append(dis[b], a)
241-
}
242351
for i := 0; i < n; i++ {
243-
for _, j := range dis[i] {
352+
for _, j := range g[i] {
244353
if find(i) == find(j) {
245354
return false
246355
}
247-
p[find(j)] = find(dis[i][0])
356+
p[find(j)] = find(g[i][0])
248357
}
249358
}
250359
return true

0 commit comments

Comments
 (0)