文章目录
- [128. 最长连续序列](https://leetcode-cn.com/problems/longest-consecutive-sequence/)
- [130. 被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions/)
- [200. 岛屿数量](https://leetcode-cn.com/problems/number-of-islands/)
- [684. 冗余连接](https://leetcode-cn.com/problems/redundant-connection/)
- [685. 冗余连接 II](https://leetcode-cn.com/problems/redundant-connection-ii/)
128. 最长连续序列
难度困难331
给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。
示例:
输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。
class Solution {
public:
struct UnionSet {
int *fa, *cnt;
UnionSet(int n) {
fa = new int[n + 1];
cnt = new int[n + 1];
for (int i = 0; i <= n; i++) {
fa[i] = i;
cnt[i] = 1;
}
}
bool isroot(int x) {
return x == fa[x];
}
int get(int x) {
return (fa[x] = (x == fa[x] ? x : get(fa[x])));
}
void merge(int a, int b) {
int aa = get(a), bb = get(b);
if (aa == bb) return ;
fa[aa] = bb;
cnt[bb] += cnt[aa];
return ;
}
~UnionSet() {
delete[] fa;
delete[] cnt;
}
};
int longestConsecutive(vector<int>& nums) {
UnionSet u(nums.size());
unordered_map<int, int> h;
for (int i = 0; i < nums.size(); i++) {
if (h.find(nums[i]) != h.end()) continue;
if (h.find(nums[i] - 1) != h.end()) {
u.merge(i, h[nums[i] - 1]);
}
if (h.find(nums[i] + 1) != h.end()) {
u.merge(i, h[nums[i] + 1]);
}
h[nums[i]] = i;
}
int ans = 0;
for (int i = 0; i < nums.size(); i++) {
if (!u.isroot(i)) continue;
ans = max(ans, u.cnt[i]);
}
return ans;
}
};
130. 被围绕的区域
难度中等213
给定一个二维的矩阵,包含 'X'
和 'O'
(字母 O)。
找到所有被 'X'
围绕的区域,并将这些区域里所有的 'O'
用 'X'
填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O'
都不会被填充为 'X'
。 任何不在边界上,或不与边界上的 'O'
相连的 'O'
最终都会被填充为 'X'
。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
class Solution {
public:
struct UnionSet {
int *fa, *cnt;
UnionSet(int n) {
fa = new int[n + 1];
cnt = new int[n + 1];
for (int i = 0; i <= n; i++) {
fa[i] = i;
cnt[i] = 1;
}
}
bool isroot(int x) {
return x == fa[x];
}
int get(int x) {
return (fa[x] = (x == fa[x] ? x : get(fa[x])));
}
void merge(int a, int b) {
int aa = get(a), bb = get(b);
if (aa == bb) return ;
fa[aa] = bb;
cnt[bb] += cnt[aa];
return ;
}
~UnionSet() {
delete[] fa;
delete[] cnt;
}
};
int n, m;
int ind(int i, int j) {
return (i * m) + j + 1;
}
void solve(vector<vector<char>>& board) {
if (board.size() == 0) return ;
n = board.size();
m = board[0].size();
UnionSet u(n * m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == 'X') continue;
if (i && board[i - 1][j] == 'O') u.merge(ind(i, j), ind(i - 1, j));
if (j && board[i][j - 1] == 'O') u.merge(ind(i, j), ind(i, j - 1));
if (i == 0 || i == n - 1) u.merge(ind(i, j), 0);
if (j == 0 || j == m - 1) u.merge(ind(i, j), 0);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == 'X') continue;
if (u.get(ind(i, j)) == u.get(0)) continue;
board[i][j] = 'X';
}
}
return ;
}
};
200. 岛屿数量
难度中等573
给你一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
class Solution {
public:
struct UnionSet {
int *fa, *cnt;
UnionSet(int n) {
fa = new int[n + 1];
cnt = new int[n + 1];
for (int i = 0; i <= n; i++) {
fa[i] = i;
cnt[i] = 1;
}
}
bool isroot(int x) {
return x == fa[x];
}
int get(int x) {
return (fa[x] = (x == fa[x] ? x : get(fa[x])));
}
void merge(int a, int b) {
int aa = get(a), bb = get(b);
if (aa == bb) return ;
fa[aa] = bb;
cnt[bb] += cnt[aa];
return ;
}
~UnionSet() {
delete[] fa;
delete[] cnt;
}
};
int n, m;
int ind(int i, int j) {
return (i * m) + j + 1;
}
int numIslands(vector<vector<char>>& grid) {
if (grid.size() == 0) return 0;
n = grid.size();
m = grid[0].size();
UnionSet u(n * m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '0') continue;
if (i && grid[i - 1][j] == '1') u.merge(ind(i, j), ind(i - 1, j));
if (j && grid[i][j - 1] == '1') u.merge(ind(i, j), ind(i, j - 1));
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '0') continue;
if (!u.isroot(ind(i, j))) continue;
ans += 1;
}
}
return ans;
}
};
684. 冗余连接
难度中等109
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边
组成的二维数组。每一个边
的元素是一对[u, v]
,满足 u < v
,表示连接顶点u
和v
的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v]
应满足相同的格式 u < v
。
示例 1:
输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
1
/ \
2 - 3
示例 2:
输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
| |
4 - 3
注意:
- 输入的二维数组大小在 3 到 1000。
- 二维数组中的整数在1到N之间,其中N是输入数组的大小。
class Solution {
public:
struct UnionSet {
int *fa;
UnionSet(int n) {
fa = new int[n + 1];
for (int i = 0; i <= n; i++) {
fa[i] = i;
}
return ;
}
int get(int x) {
return (fa[x] = (x - fa[x] ? get(fa[x]) : x));
}
int merge(int a, int b) {
if (get(a) == get(b)) return 0;
fa[get(a)] = get(b);
return 1;
}
};
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
UnionSet u(edges.size());
vector<int> ret;
for (int i = 0; i < edges.size(); i++) {
int a = edges[i][0];
int b = edges[i][1];
if (u.merge(a, b)) continue;//联通时候
ret.push_back(a);//不连通记录下来
ret.push_back(b);//不连通记录下来
break;
}
return ret;
}
};
685. 冗余连接 II
难度困难62
在本问题中,有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点。
输入一个有向图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边
组成的二维数组。 每一个边
的元素是一对 [u, v]
,用以表示有向图中连接顶点 u
and v
和顶点的边,其中父节点u
是子节点v
的一个父节点。
返回一条能删除的边,使得剩下的图是有N个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。
示例 1:
输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的有向图如下:
1
/ \
v v
2-->3
示例 2:
输入: [[1,2], [2,3], [3,4], [4,1], [1,5]]
输出: [4,1]
解释: 给定的有向图如下:
5 <- 1 -> 2
^ |
| v
4 <- 3
注意:
- 二维数组大小的在3到1000范围内。
- 二维数组中的每个整数在1到N之间,其中 N 是二维数组的大小。
class Solution {
public:
struct UnionSet {
int *fa, cnt;
UnionSet(int n) {
fa = new int[n + 1];
for (int i = 0; i <= n; i++) {
fa[i] = i;
}
cnt = n;
return ;
}
int get(int x) {
return (fa[x] = (x - fa[x] ? get(fa[x]) : x));
}
int merge(int a, int b) {
if (get(a) == get(b)) return 0;
cnt -= 1;
fa[get(a)] = get(b);
return 1;
}
};
vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
int indeg[edges.size() + 1];
int outdeg[edges.size() + 1];
int fa[edges.size() + 1];
int vis[edges.size() + 1];
memset(indeg, 0, sizeof(indeg));
memset(outdeg, 0, sizeof(outdeg));
memset(fa, 0, sizeof(fa));
memset(vis, 0, sizeof(vis));
int flag = -1;
for (int i = 0; i < edges.size(); i++) {
int u = edges[i][0];
int v = edges[i][1];
indeg[v] += 1;
outdeg[u] += 1;
fa[v] = u;
if (indeg[v] == 2) flag = v;
}
if (flag != -1) {
for (int i = edges.size() - 1; i >= 0; i--) {
if (flag - edges[i][1]) continue;
UnionSet u(edges.size());
for (int j = 0; j < edges.size(); j++) {
if (i == j) continue;
u.merge(edges[j][0], edges[j][1]);
}
if (u.cnt != 1) continue;
return edges[i];
}
}
queue<int> q;
for (int i = 1; i <= edges.size(); i++) {
if (outdeg[i] == 0) q.push(i);
}
while (!q.empty()) {
int ind = q.front();
vis[ind] = 1;
q.pop();
outdeg[fa[ind]] -= 1;
if (outdeg[fa[ind]] == 0) q.push(fa[ind]);
}
for (int i = edges.size() - 1; i >= 0; i--) {
int u = edges[i][0];
int v = edges[i][1];
if (vis[u] || vis[v]) continue;
return edges[i];
}
return edges[0]; // 没用的
}
};