Welcome to Subscribe On Youtube
886. Possible Bipartition
Description
We want to split a group of n people (labeled from 1 to n) into two groups of any size. Each person may dislike some other people, and they should not go into the same group.
Given the integer n and the array dislikes where dislikes[i] = [ai, bi] indicates that the person labeled ai does not like the person labeled bi, return true if it is possible to split everyone into two groups in this way.
Example 1:
Input: n = 4, dislikes = [[1,2],[1,3],[2,4]] Output: true Explanation: The first group has [1,4], and the second group has [2,3].
Example 2:
Input: n = 3, dislikes = [[1,2],[1,3],[2,3]] Output: false Explanation: We need at least 3 groups to divide them. We cannot put them in two groups.
Constraints:
1 <= n <= 20000 <= dislikes.length <= 104dislikes[i].length == 21 <= ai < bi <= n- All the pairs of
dislikesare unique.
Solutions
Solution 1
Union find.
Solution 2
Union-find is a tree-shaped data structure. As the name suggests, it is used to deal with some disjoint merge and query problems. It supports two operations:
- Find: Determine which subset an element is in, the time complexity of a single operation is $O(\alpha(n))$
- Union: Merge two subsets into one set, the time complexity of a single operation is $O(\alpha(n))$
Among them, $\alpha$ is the inverse function of Ackerman function, and its growth is extremely slow, which means that the average running time of a single operation can be considered as a small constant.
The following are commonly used templates for union search, which require proficiency. Among them:
nrepresents the number of nodespstores the parent node of each point. Initially, the parent node of each point is itself.sizeis only meaningful when the node is an ancestor node, indicating the number of points in the set where the ancestor node is located- The
find(x)function is used to find the ancestor node of the set where $x$ is located - The
union(a, b)function is used to merge the collections where $a$ and $b$ are located
p = list(range(n))
size = [1] * n
def find(x):
if p[x] != x:
# 路径压缩
p[x] = find(p[x])
return p[x]
def union(a, b):
pa, pb = find(a), find(b)
if pa == pb:
return
p[pa] = pb
size[pb] += size[pa]
For this question, we traverse each person. He and the person he dislikes should not be in the same set. If they are in the same set, a conflict will occur and false will be returned directly. If there are no conflicts, then merge all his disliked ones into the same set.
The traversal ends, indicating that there is no conflict, and true is returned.
Time complexity $O(n + m\times \alpha(n))$.
Solution 3: Explicit-Stack Coloring
Thinking
People who dislike each other cannot share a group, so the dislike graph must be bipartite. With $n\le 2000$, recursion along a chain of dislikes uses a call depth equal to the number of people and overflows Python once the chain reaches length $1000$. A graph is bipartite exactly when two colors can cover it with every edge joining different colors. A stack therefore expands each component: an uncolored person is colored $1$ and pushed, a popped person fails the search when a neighbor already has the same color, and an uncolored neighbor is colored $3$ minus the current color and pushed. A partition exists when every component is colored.
-
class Solution { private int[] p; public boolean possibleBipartition(int n, int[][] dislikes) { p = new int[n]; List<Integer>[] g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (int i = 0; i < n; ++i) { p[i] = i; } for (var e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].add(b); g[b].add(a); } for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) { return false; } p[find(j)] = find(g[i].get(0)); } } return true; } private int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } } // Solution 2 class Solution { private int[] p; public boolean possibleBipartition(int n, int[][] dislikes) { p = new int[n]; List<Integer>[] g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (int i = 0; i < n; ++i) { p[i] = i; } for (var e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].add(b); g[b].add(a); } for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) { return false; } p[find(j)] = find(g[i].get(0)); } } return true; } private int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } } // Solution 3 class Solution { public boolean possibleBipartition(int n, int[][] dislikes) { List<Integer>[] g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (var e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].add(b); g[b].add(a); } int[] color = new int[n]; for (int start = 0; start < n; ++start) { if (color[start] != 0) { continue; } color[start] = 1; Deque<Integer> stk = new ArrayDeque<>(); stk.push(start); while (!stk.isEmpty()) { int i = stk.pop(); for (int j : g[i]) { if (color[j] == color[i]) { return false; } if (color[j] == 0) { color[j] = 3 - color[i]; stk.push(j); } } } } return true; } } -
class Solution { public: bool possibleBipartition(int n, vector<vector<int>>& dislikes) { vector<int> p(n); iota(p.begin(), p.end(), 0); unordered_map<int, vector<int>> g; for (auto& e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].push_back(b); g[b].push_back(a); } function<int(int)> find = [&](int x) -> int { if (p[x] != x) p[x] = find(p[x]); return p[x]; }; for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) return false; p[find(j)] = find(g[i][0]); } } return true; } }; // Solution 2 class Solution { public: bool possibleBipartition(int n, vector<vector<int>>& dislikes) { vector<int> p(n); iota(p.begin(), p.end(), 0); unordered_map<int, vector<int>> g; for (auto& e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].push_back(b); g[b].push_back(a); } function<int(int)> find = [&](int x) -> int { if (p[x] != x) p[x] = find(p[x]); return p[x]; }; for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) return false; p[find(j)] = find(g[i][0]); } } return true; } }; // Solution 3 class Solution { public: bool possibleBipartition(int n, vector<vector<int>>& dislikes) { vector<vector<int>> g(n); for (auto& e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].push_back(b); g[b].push_back(a); } vector<int> color(n); for (int start = 0; start < n; ++start) { if (color[start]) { continue; } color[start] = 1; vector<int> stk{start}; while (!stk.empty()) { int i = stk.back(); stk.pop_back(); for (int j : g[i]) { if (color[j] == color[i]) { return false; } if (color[j] == 0) { color[j] = 3 - color[i]; stk.push_back(j); } } } } return true; } }; -
class Solution: def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool: def find(x): if p[x] != x: p[x] = find(p[x]) return p[x] g = defaultdict(list) for a, b in dislikes: a, b = a - 1, b - 1 g[a].append(b) g[b].append(a) p = list(range(n)) for i in range(n): for j in g[i]: if find(i) == find(j): return False p[find(j)] = find(g[i][0]) return True # Solution 2 class Solution: def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool: def find(x): if p[x] != x: p[x] = find(p[x]) return p[x] g = defaultdict(list) for a, b in dislikes: a, b = a - 1, b - 1 g[a].append(b) g[b].append(a) p = list(range(n)) for i in range(n): for j in g[i]: if find(i) == find(j): return False p[find(j)] = find(g[i][0]) return True # Solution 3 class Solution: def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool: g = defaultdict(list) for a, b in dislikes: a, b = a - 1, b - 1 g[a].append(b) g[b].append(a) color = [0] * n for start in range(n): if color[start]: continue color[start] = 1 stk = [start] while stk: i = stk.pop() for j in g[i]: if color[j] == color[i]: return False if color[j] == 0: color[j] = 3 - color[i] stk.append(j) return True -
func possibleBipartition(n int, dislikes [][]int) bool { p := make([]int, n) g := make([][]int, n) for i := range p { p[i] = i } for _, e := range dislikes { a, b := e[0]-1, e[1]-1 g[a] = append(g[a], b) g[b] = append(g[b], a) } var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) } return p[x] } for i := 0; i < n; i++ { for _, j := range g[i] { if find(i) == find(j) { return false } p[find(j)] = find(g[i][0]) } } return true } // Solution 2 func possibleBipartition(n int, dislikes [][]int) bool { p := make([]int, n) g := make([][]int, n) for i := range p { p[i] = i } for _, e := range dislikes { a, b := e[0]-1, e[1]-1 g[a] = append(g[a], b) g[b] = append(g[b], a) } var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) } return p[x] } for i := 0; i < n; i++ { for _, j := range g[i] { if find(i) == find(j) { return false } p[find(j)] = find(g[i][0]) } } return true } // Solution 3 func possibleBipartition(n int, dislikes [][]int) bool { g := make([][]int, n) for _, e := range dislikes { a, b := e[0]-1, e[1]-1 g[a] = append(g[a], b) g[b] = append(g[b], a) } color := make([]int, n) for start := 0; start < n; start++ { if color[start] != 0 { continue } color[start] = 1 stk := []int{start} for len(stk) > 0 { i := stk[len(stk)-1] stk = stk[:len(stk)-1] for _, j := range g[i] { if color[j] == color[i] { return false } if color[j] == 0 { color[j] = 3 - color[i] stk = append(stk, j) } } } } return true } -
function possibleBipartition(n: number, dislikes: number[][]): boolean { const color = new Array(n + 1).fill(0); const g = Array.from({ length: n + 1 }, () => []); const dfs = (i: number, v: number) => { color[i] = v; for (const j of g[i]) { if (color[j] === color[i] || (color[j] === 0 && dfs(j, 3 ^ v))) { return true; } } return false; }; for (const [a, b] of dislikes) { g[a].push(b); g[b].push(a); } for (let i = 1; i <= n; i++) { if (color[i] === 0 && dfs(i, 1)) { return false; } } return true; } // Solution 3 function possibleBipartition(n: number, dislikes: number[][]): boolean { const g: number[][] = Array.from({ length: n }, () => []); for (const [a, b] of dislikes) { g[a - 1].push(b - 1); g[b - 1].push(a - 1); } const color: number[] = new Array(n).fill(0); for (let start = 0; start < n; start++) { if (color[start] !== 0) { continue; } color[start] = 1; const stk: number[] = [start]; while (stk.length) { const i = stk.pop()!; for (const j of g[i]) { if (color[j] === color[i]) { return false; } if (color[j] === 0) { color[j] = 3 - color[i]; stk.push(j); } } } } return true; } -
impl Solution { fn dfs(i: usize, v: usize, color: &mut Vec<usize>, g: &Vec<Vec<usize>>) -> bool { color[i] = v; for &j in (*g[i]).iter() { if color[j] == color[i] || (color[j] == 0 && Self::dfs(j, v ^ 3, color, g)) { return true; } } false } pub fn possible_bipartition(n: i32, dislikes: Vec<Vec<i32>>) -> bool { let n = n as usize; let mut color = vec![0; n + 1]; let mut g = vec![Vec::new(); n + 1]; for d in dislikes.iter() { let (i, j) = (d[0] as usize, d[1] as usize); g[i].push(j); g[j].push(i); } for i in 1..=n { if color[i] == 0 && Self::dfs(i, 1, &mut color, &g) { return false; } } true } } // Solution 3 impl Solution { pub fn possible_bipartition(n: i32, dislikes: Vec<Vec<i32>>) -> bool { let n = n as usize; let mut g = vec![Vec::new(); n]; for d in dislikes.iter() { let a = d[0] as usize - 1; let b = d[1] as usize - 1; g[a].push(b); g[b].push(a); } let mut color = vec![0; n]; for start in 0..n { if color[start] != 0 { continue; } color[start] = 1; let mut stk = vec![start]; while let Some(i) = stk.pop() { for &j in &g[i] { if color[j] == color[i] { return false; } if color[j] == 0 { color[j] = 3 - color[i]; stk.push(j); } } } } true } }