【2022-10-16每日一题】886. 可能的二分法[Medium]
2022-10-16
3分钟阅读时长
2022-10-16每日一题:886. 可能的二分法
难度:Medium
标签:深度优先搜索 、 广度优先搜索 、 并查集 、 图
给定一组 n
人(编号为 1, 2, ..., n
), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。
给定整数 n
和数组 dislikes
,其中 dislikes[i] = [ai, bi]
,表示不允许将编号为 ai
和 bi
的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true
;否则返回 false
。
示例 1:
输入:n = 4, dislikes = [[1,2],[1,3],[2,4]] 输出:true 解释:group1 [1,4], group2 [2,3]
示例 2:
输入:n = 3, dislikes = [[1,2],[1,3],[2,3]] 输出:false
示例 3:
输入:n = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]] 输出:false
提示:
1 <= n <= 2000
0 <= dislikes.length <= 104
dislikes[i].length == 2
1 <= dislikes[i][j] <= n
ai < bi
dislikes
中每一组都 不同
方法一:染色法+深度优先搜索
详细思路过程见官方题解,这里只做个人刷题记录,方便后续查询阅读
func possibleBipartition(n int, dislikes [][]int) bool {
g := make([][]int, n)
for _, d := range dislikes {
x, y := d[0]-1, d[1]-1 // 1~n 转换为 0~n-1
g[x] = append(g[x], y)
g[y] = append(g[y], x)
}
color := make([]int, n) // color[x] = 0 表示未访问节点 x, 1 红色,2蓝色
var dfs func(x, c int) bool
dfs = func(x, c int) bool {
color[x] = c
for _, y := range g[x] {
// 颜色相同 或者 染色失败 返回 false
// 3^c: 如果c=1结果为2, 如果c=2结果为1
if color[x] == color[y] || color[y] == 0 && !dfs(y, 3^c) {
return false
}
}
return true
}
for i, c := range color {
// 染色
if c == 0 && !dfs(i, 1) {
return false
}
}
return true
}
复杂度分析
- 时间复杂度:O(n + m),其中 n 题目给定的人数,m 为给定的 dislike 数组的大小。
- 空间复杂度:O(n + m),其中 n 题目给定的人数,m 为给定的 dislike 数组的大小。
方法二:染色法+广度优先搜索
func possibleBipartition(n int, dislikes [][]int) bool {
g := make([][]int, n)
for _, d := range dislikes {
x, y := d[0]-1, d[1]-1
g[x] = append(g[x], y)
g[y] = append(g[y], x)
}
// color[x] = 0 标识未染色 1红色,-1蓝色
color := make([]int, n)
for i, c := range color {
// 未染色
if c == 0 {
color[i] = 1
q := []int{i}
for len(q) > 0 {
x := q[0]
q = q[1:]
// 取出对立面进行染色
for _, y := range g[x] {
if color[x] == color[y] {
return false
}
if color[y] == 0 {
color[y] = -color[x]
q = append(q, y)
}
}
}
}
}
return true
}
复杂度分析
- 时间复杂度:O(n + m),其中 n 题目给定的人数,m 为给定的 dislike 数组的大小。
- 空间复杂度:O(n + m),其中 n 题目给定的人数,m 为给定的 dislike 数组的大小。
方法三:并查集
// 并查集实现模板
type UnionFind struct {
parent, rank []int
}
func newUnionFind(n int) UnionFind {
parent := make([]int, n)
for i := range parent {
parent[i] = i
}
return UnionFind{parent, make([]int, n)}
}
func (uf UnionFind) find(x int) int {
if uf.parent[x] != x {
// 路径压缩
uf.parent[x] = uf.find(uf.parent[x])
}
return uf.parent[x]
}
func (uf UnionFind) union(x, y int) {
x, y = uf.find(x), uf.find(y)
if x == y {
return
}
// 路径压缩优化
if uf.rank[x] > uf.rank[y] {
uf.parent[y] = x
} else if uf.rank[x] < uf.rank[y] {
uf.parent[x] = y
} else {
uf.parent[y] = x
uf.rank[x]++
}
}
func (uf UnionFind) isConnectted(x, y int) bool {
return uf.find(x) == uf.find(y)
}
func possibleBipartition(n int, dislikes [][]int) bool {
g := make([][]int, n)
for _, d := range dislikes {
x, y := d[0]-1, d[1]-1
g[x] = append(g[x], y)
g[y] = append(g[y], x)
}
uf := newUnionFind(n)
for x, nodes := range g {
for _, y := range nodes {
uf.union(nodes[0], y) // 相当于染色
if uf.isConnectted(x, y) {
return false
}
}
}
return true
}
复杂度分析
- 时间复杂度:O(n + mα(n)),其中 n 题目给定的人数,m 为给定的 dislike 数组的大小,α 是反 Ackerman 函数。
- 空间复杂度:O(n + m),其中 n 题目给定的人数,m 为给定的 dislike 数组的大小。