diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README.md b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README.md index b85e6131df0c1..90f7b5ef95b96 100644 --- a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README.md +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README.md @@ -74,113 +74,531 @@ tags: -### 方法一 +### 方法一:并查集 + +我们可以用并查集维护石头之间的关系。如果两块石头在同一行或同一列,我们就认为它们之间有关系,可以通过并查集将它们连接起来。最后,我们统计并查集中有多少个连通分量,这个数值就是可以剩余的石头的数量,那么总共可以移除的石头数量就是石头总数减去剩余的石头数量。我们也可以在合并的时候,记录成功合并的次数,这个次数就是可以移除的石头的数量。 + +时间复杂度 $O(n^2 \times \alpha(n))$,空间复杂度 $O(n)$。其中 $n$ 为石头的数量。 #### Python3 ```python -class Solution: - def removeStones(self, stones: List[List[int]]) -> int: - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True - n = 10010 - p = list(range(n << 1)) - for x, y in stones: - p[find(x)] = find(y + n) - s = {find(x) for x, _ in stones} - return len(stones) - len(s) +class Solution: + def removeStones(self, stones: List[List[int]]) -> int: + uf = UnionFind(len(stones)) + ans = 0 + for i, (x1, y1) in enumerate(stones): + for j, (x2, y2) in enumerate(stones[:i]): + if x1 == x2 or y1 == y2: + ans += uf.union(i, j) + return ans ``` #### Java ```java -class Solution { - private int[] p; +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + size[i] = 1; + } + } + public int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { public int removeStones(int[][] stones) { - int n = 10010; - p = new int[n << 1]; - for (int i = 0; i < p.length; ++i) { - p[i] = i; + int n = stones.length; + UnionFind uf = new UnionFind(n); + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + ans += uf.union(i, j) ? 1 : 0; + } + } } - for (int[] stone : stones) { - p[find(stone[0])] = find(stone[1] + n); + return ans; + } +} +``` + +#### C++ + +```cpp +class UnionFind { +public: + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } + + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; } - Set s = new HashSet<>(); - for (int[] stone : stones) { - s.add(find(stone[0])); + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + int removeStones(vector>& stones) { + int n = stones.size(); + UnionFind uf(n); + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + ans += uf.unite(i, j); + } + } + } + return ans; + } +}; +``` + +#### Go + +```go +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) + for i := range p { + p[i] = i + size[i] = 1 + } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func removeStones(stones [][]int) (ans int) { + n := len(stones) + uf := newUnionFind(n) + for i, s1 := range stones { + for j, s2 := range stones[:i] { + if s1[0] == s2[0] || s1[1] == s2[1] { + if uf.union(i, j) { + ans++ + } + } + } + } + return +} +``` + +#### TypeScript + +```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array.from({ length: n }, (_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function removeStones(stones: number[][]): number { + const n = stones.length; + const uf = new UnionFind(n); + let ans = 0; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < i; ++j) { + if (stones[i][0] === stones[j][0] || stones[i][1] === stones[j][1]) { + ans += uf.union(i, j) ? 1 : 0; + } + } + } + return ans; +} +``` + + + + + + + +### 方法二:并查集(优化) + +我们可以将石头的纵坐标加上一个偏移量,这样就可以将横坐标和纵坐标统一起来,然后用并查集维护横坐标和纵坐标之间的关系。 + +我们遍历每一块石头,将横坐标与纵坐标进行合并。 + +最后,我们再遍历所有石头,将每块石头的横坐标的根节点放到一个集合中,那么这个集合的数量就是可以剩余的石头的数量,总共可以移除的石头数量就是石头总数减去剩余的石头数量。 + +时间复杂度 $O(n \times \alpha(m))$,空间复杂度 $O(m)$。其中 $n$ 和 $m$ 分别为石头的数量和横纵坐标的最大值。 + + + +#### Python3 + +```python +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + +class Solution: + def removeStones(self, stones: List[List[int]]) -> int: + m = 10001 + uf = UnionFind(m << 1) + for x, y in stones: + uf.union(x, y + m) + return len(stones) - len({uf.find(x) for x, _ in stones}) +``` + +#### Java + +```java +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + size[i] = 1; } - return stones.length - s.size(); } - private int find(int x) { + public int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { + public int removeStones(int[][] stones) { + int m = 10001; + UnionFind uf = new UnionFind(m << 1); + for (var st : stones) { + uf.union(st[0], st[1] + m); + } + Set s = new HashSet<>(); + for (var st : stones) { + s.add(uf.find(st[0])); + } + return stones.length - s.size(); + } } ``` #### C++ ```cpp -class Solution { +class UnionFind { public: - vector p; + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } - int removeStones(vector>& stones) { - int n = 10010; - p.resize(n << 1); - for (int i = 0; i < p.size(); ++i) p[i] = i; - for (auto& stone : stones) p[find(stone[0])] = find(stone[1] + n); - unordered_set s; - for (auto& stone : stones) s.insert(find(stone[0])); - return stones.size() - s.size(); + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; } int find(int x) { - if (p[x] != x) p[x] = find(p[x]); + if (p[x] != x) { + p[x] = find(p[x]); + } return p[x]; } + +private: + vector p, size; +}; + +class Solution { +public: + int removeStones(vector>& stones) { + int m = 10001; + UnionFind uf(m << 1); + for (auto& st : stones) { + uf.unite(st[0], st[1] + m); + } + unordered_set s; + for (auto& st : stones) { + s.insert(uf.find(st[0])); + } + return stones.size() - s.size(); + } }; ``` #### Go ```go -func removeStones(stones [][]int) int { - n := 10010 - p := make([]int, n<<1) +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) for i := range p { p[i] = i + size[i] = 1 } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) } - for _, stone := range stones { - p[find(stone[0])] = find(stone[1] + n) + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func removeStones(stones [][]int) (ans int) { + m := 10001 + uf := newUnionFind(m << 1) + for _, st := range stones { + uf.union(st[0], st[1]+m) } - s := make(map[int]bool) - for _, stone := range stones { - s[find(stone[0])] = true + s := map[int]bool{} + for _, st := range stones { + s[uf.find(st[0])] = true } return len(stones) - len(s) } ``` +#### TypeScript + +```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array.from({ length: n }, (_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function removeStones(stones: number[][]): number { + const m = 10001; + const uf = new UnionFind(m << 1); + for (const [x, y] of stones) { + uf.union(x, y + m); + } + const s = new Set(); + for (const [x, _] of stones) { + s.add(uf.find(x)); + } + return stones.length - s.size; +} +``` + - + diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README_EN.md b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README_EN.md index da5f8f7d95fa5..500e5ced4bc29 100644 --- a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README_EN.md +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/README_EN.md @@ -75,113 +75,531 @@ Stones [0,0] and [1,1] cannot be removed since they do not share a row/column wi -### Solution 1 +### Solution 1: Union-Find + +We can use a union-find data structure to maintain the relationships between stones. If two stones are in the same row or column, we consider them to be connected and use the union-find to link them together. In the end, we count how many connected components there are in the union-find, which corresponds to the number of stones that can remain. Therefore, the total number of stones that can be removed is the total number of stones minus the number of stones that can remain. We can also record the number of successful unions during the merge process, which equals the number of stones that can be removed. + +The time complexity is $O(n^2 \times \alpha(n))$, and the space complexity is $O(n)$. Here, $n$ is the number of stones. #### Python3 ```python -class Solution: - def removeStones(self, stones: List[List[int]]) -> int: - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True - n = 10010 - p = list(range(n << 1)) - for x, y in stones: - p[find(x)] = find(y + n) - s = {find(x) for x, _ in stones} - return len(stones) - len(s) +class Solution: + def removeStones(self, stones: List[List[int]]) -> int: + uf = UnionFind(len(stones)) + ans = 0 + for i, (x1, y1) in enumerate(stones): + for j, (x2, y2) in enumerate(stones[:i]): + if x1 == x2 or y1 == y2: + ans += uf.union(i, j) + return ans ``` #### Java ```java -class Solution { - private int[] p; +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + size[i] = 1; + } + } + public int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { public int removeStones(int[][] stones) { - int n = 10010; - p = new int[n << 1]; - for (int i = 0; i < p.length; ++i) { - p[i] = i; + int n = stones.length; + UnionFind uf = new UnionFind(n); + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + ans += uf.union(i, j) ? 1 : 0; + } + } } - for (int[] stone : stones) { - p[find(stone[0])] = find(stone[1] + n); + return ans; + } +} +``` + +#### C++ + +```cpp +class UnionFind { +public: + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } + + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; } - Set s = new HashSet<>(); - for (int[] stone : stones) { - s.add(find(stone[0])); + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + int removeStones(vector>& stones) { + int n = stones.size(); + UnionFind uf(n); + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + ans += uf.unite(i, j); + } + } + } + return ans; + } +}; +``` + +#### Go + +```go +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) + for i := range p { + p[i] = i + size[i] = 1 + } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func removeStones(stones [][]int) (ans int) { + n := len(stones) + uf := newUnionFind(n) + for i, s1 := range stones { + for j, s2 := range stones[:i] { + if s1[0] == s2[0] || s1[1] == s2[1] { + if uf.union(i, j) { + ans++ + } + } + } + } + return +} +``` + +#### TypeScript + +```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array.from({ length: n }, (_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function removeStones(stones: number[][]): number { + const n = stones.length; + const uf = new UnionFind(n); + let ans = 0; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < i; ++j) { + if (stones[i][0] === stones[j][0] || stones[i][1] === stones[j][1]) { + ans += uf.union(i, j) ? 1 : 0; + } + } + } + return ans; +} +``` + + + + + + + +### Solution 2: Union-Find (Optimized) + +We can add an offset to the y-coordinates of the stones, allowing us to unify the x-coordinates and y-coordinates. Then, we use a union-find data structure to maintain the relationship between x-coordinates and y-coordinates. + +We iterate through each stone, merging its x-coordinate with its y-coordinate. + +Finally, we iterate through all the stones again, putting the root node of each stone's x-coordinate into a set. The number of elements in this set represents the number of stones that can remain. Therefore, the total number of stones that can be removed is the total number of stones minus the number of stones that can remain. + +The time complexity is $O(n \times \alpha(m))$, and the space complexity is $O(m)$. Here, $n$ and $m$ represent the number of stones and the maximum value of the coordinates, respectively. + + + +#### Python3 + +```python +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + +class Solution: + def removeStones(self, stones: List[List[int]]) -> int: + m = 10001 + uf = UnionFind(m << 1) + for x, y in stones: + uf.union(x, y + m) + return len(stones) - len({uf.find(x) for x, _ in stones}) +``` + +#### Java + +```java +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + size[i] = 1; } - return stones.length - s.size(); } - private int find(int x) { + public int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { + public int removeStones(int[][] stones) { + int m = 10001; + UnionFind uf = new UnionFind(m << 1); + for (var st : stones) { + uf.union(st[0], st[1] + m); + } + Set s = new HashSet<>(); + for (var st : stones) { + s.add(uf.find(st[0])); + } + return stones.length - s.size(); + } } ``` #### C++ ```cpp -class Solution { +class UnionFind { public: - vector p; + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } - int removeStones(vector>& stones) { - int n = 10010; - p.resize(n << 1); - for (int i = 0; i < p.size(); ++i) p[i] = i; - for (auto& stone : stones) p[find(stone[0])] = find(stone[1] + n); - unordered_set s; - for (auto& stone : stones) s.insert(find(stone[0])); - return stones.size() - s.size(); + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; } int find(int x) { - if (p[x] != x) p[x] = find(p[x]); + if (p[x] != x) { + p[x] = find(p[x]); + } return p[x]; } + +private: + vector p, size; +}; + +class Solution { +public: + int removeStones(vector>& stones) { + int m = 10001; + UnionFind uf(m << 1); + for (auto& st : stones) { + uf.unite(st[0], st[1] + m); + } + unordered_set s; + for (auto& st : stones) { + s.insert(uf.find(st[0])); + } + return stones.size() - s.size(); + } }; ``` #### Go ```go -func removeStones(stones [][]int) int { - n := 10010 - p := make([]int, n<<1) +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) for i := range p { p[i] = i + size[i] = 1 } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) } - for _, stone := range stones { - p[find(stone[0])] = find(stone[1] + n) + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func removeStones(stones [][]int) (ans int) { + m := 10001 + uf := newUnionFind(m << 1) + for _, st := range stones { + uf.union(st[0], st[1]+m) } - s := make(map[int]bool) - for _, stone := range stones { - s[find(stone[0])] = true + s := map[int]bool{} + for _, st := range stones { + s[uf.find(st[0])] = true } return len(stones) - len(s) } ``` +#### TypeScript + +```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array.from({ length: n }, (_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function removeStones(stones: number[][]): number { + const m = 10001; + const uf = new UnionFind(m << 1); + for (const [x, y] of stones) { + uf.union(x, y + m); + } + const s = new Set(); + for (const [x, _] of stones) { + s.add(uf.find(x)); + } + return stones.length - s.size; +} +``` + - + diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.cpp b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.cpp index 65b9cc19faeb8..964f7cc9edd1f 100644 --- a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.cpp +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.cpp @@ -1,19 +1,50 @@ -class Solution { +class UnionFind { public: - vector p; + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } - int removeStones(vector>& stones) { - int n = 10010; - p.resize(n << 1); - for (int i = 0; i < p.size(); ++i) p[i] = i; - for (auto& stone : stones) p[find(stone[0])] = find(stone[1] + n); - unordered_set s; - for (auto& stone : stones) s.insert(find(stone[0])); - return stones.size() - s.size(); + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; } int find(int x) { - if (p[x] != x) p[x] = find(p[x]); + if (p[x] != x) { + p[x] = find(p[x]); + } return p[x]; } + +private: + vector p, size; +}; + +class Solution { +public: + int removeStones(vector>& stones) { + int n = stones.size(); + UnionFind uf(n); + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + ans += uf.unite(i, j); + } + } + } + return ans; + } }; \ No newline at end of file diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.go b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.go index d26c7d3befbba..237421508eadd 100644 --- a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.go +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.go @@ -1,22 +1,50 @@ -func removeStones(stones [][]int) int { - n := 10010 - p := make([]int, n<<1) +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) for i := range p { p[i] = i + size[i] = 1 } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false } - for _, stone := range stones { - p[find(stone[0])] = find(stone[1] + n) + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] } - s := make(map[int]bool) - for _, stone := range stones { - s[find(stone[0])] = true + return true +} + +func removeStones(stones [][]int) (ans int) { + n := len(stones) + uf := newUnionFind(n) + for i, s1 := range stones { + for j, s2 := range stones[:i] { + if s1[0] == s2[0] || s1[1] == s2[1] { + if uf.union(i, j) { + ans++ + } + } + } } - return len(stones) - len(s) + return } \ No newline at end of file diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.java b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.java index c58d5670722ee..6360d5bcccff2 100644 --- a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.java +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.java @@ -1,26 +1,51 @@ -class Solution { - private int[] p; +class UnionFind { + private final int[] p; + private final int[] size; - public int removeStones(int[][] stones) { - int n = 10010; - p = new int[n << 1]; - for (int i = 0; i < p.length; ++i) { + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { p[i] = i; + size[i] = 1; } - for (int[] stone : stones) { - p[find(stone[0])] = find(stone[1] + n); - } - Set s = new HashSet<>(); - for (int[] stone : stones) { - s.add(find(stone[0])); - } - return stones.length - s.size(); } - private int find(int x) { + public int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { + public int removeStones(int[][] stones) { + int n = stones.length; + UnionFind uf = new UnionFind(n); + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + ans += uf.union(i, j) ? 1 : 0; + } + } + } + return ans; + } } \ No newline at end of file diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.py b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.py index d9e7e561703ac..2caf23d6f21b3 100644 --- a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.py +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.py @@ -1,14 +1,32 @@ +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + class Solution: def removeStones(self, stones: List[List[int]]) -> int: - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] - - n = 10010 - p = list(range(n << 1)) - for x, y in stones: - p[find(x)] = find(y + n) - - s = {find(x) for x, _ in stones} - return len(stones) - len(s) + uf = UnionFind(len(stones)) + ans = 0 + for i, (x1, y1) in enumerate(stones): + for j, (x2, y2) in enumerate(stones[:i]): + if x1 == x2 or y1 == y2: + ans += uf.union(i, j) + return ans diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.ts b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.ts new file mode 100644 index 0000000000000..bad87164fe9a4 --- /dev/null +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution.ts @@ -0,0 +1,44 @@ +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array.from({ length: n }, (_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function removeStones(stones: number[][]): number { + const n = stones.length; + const uf = new UnionFind(n); + let ans = 0; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < i; ++j) { + if (stones[i][0] === stones[j][0] || stones[i][1] === stones[j][1]) { + ans += uf.union(i, j) ? 1 : 0; + } + } + } + return ans; +} diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.cpp b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.cpp new file mode 100644 index 0000000000000..b7c8830831166 --- /dev/null +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.cpp @@ -0,0 +1,49 @@ +class UnionFind { +public: + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } + + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + int removeStones(vector>& stones) { + int m = 10001; + UnionFind uf(m << 1); + for (auto& st : stones) { + uf.unite(st[0], st[1] + m); + } + unordered_set s; + for (auto& st : stones) { + s.insert(uf.find(st[0])); + } + return stones.size() - s.size(); + } +}; \ No newline at end of file diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.go b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.go new file mode 100644 index 0000000000000..0f71b8f2584d6 --- /dev/null +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.go @@ -0,0 +1,48 @@ +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) + for i := range p { + p[i] = i + size[i] = 1 + } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func removeStones(stones [][]int) (ans int) { + m := 10001 + uf := newUnionFind(m << 1) + for _, st := range stones { + uf.union(st[0], st[1]+m) + } + s := map[int]bool{} + for _, st := range stones { + s[uf.find(st[0])] = true + } + return len(stones) - len(s) +} \ No newline at end of file diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.java b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.java new file mode 100644 index 0000000000000..fad77730e9672 --- /dev/null +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.java @@ -0,0 +1,50 @@ +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + size[i] = 1; + } + } + + public int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { + public int removeStones(int[][] stones) { + int m = 10001; + UnionFind uf = new UnionFind(m << 1); + for (var st : stones) { + uf.union(st[0], st[1] + m); + } + Set s = new HashSet<>(); + for (var st : stones) { + s.add(uf.find(st[0])); + } + return stones.length - s.size(); + } +} \ No newline at end of file diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.py b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.py new file mode 100644 index 0000000000000..a1ae149073689 --- /dev/null +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.py @@ -0,0 +1,30 @@ +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + +class Solution: + def removeStones(self, stones: List[List[int]]) -> int: + m = 10001 + uf = UnionFind(m << 1) + for x, y in stones: + uf.union(x, y + m) + return len(stones) - len({uf.find(x) for x, _ in stones}) diff --git a/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.ts b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.ts new file mode 100644 index 0000000000000..83a5fbf69b38e --- /dev/null +++ b/solution/0900-0999/0947.Most Stones Removed with Same Row or Column/Solution2.ts @@ -0,0 +1,43 @@ +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array.from({ length: n }, (_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function removeStones(stones: number[][]): number { + const m = 10001; + const uf = new UnionFind(m << 1); + for (const [x, y] of stones) { + uf.union(x, y + m); + } + const s = new Set(); + for (const [x, _] of stones) { + s.add(uf.find(x)); + } + return stones.length - s.size; +}