对有向图的节点排序,使得对于每一条有向边 U-->V U都出现在V之前
*有环无法拓扑排序
indegree[], nxs[];//前者表示节点 i 的入度,后者表示节点 i 指向的节点
queue = []
for i in range(n):
if indege[i] == 0: queue.add(i)// 入度为0的节点加入队列
while queue:
curnode = queue.popleft()
for nx in nxs[curnode]:
indegre[nx] -= 1;
if indegre[nx] == 0:
queue.add(nx);
#include
#include
using namespace std;
class Solution {
public:
bool canFinish(int numCourses, vector>& prerequisites) {
// 邻接表
vector> nxs(numCourses, vector());
// 入度数组
vector indegree(numCourses, 0);
// 填充入度数组和邻接表
for (auto pre : prerequisites) {
int a = pre[0];
int b = pre[1];
// a 的入度增加
indegree[a]++;
// 将 b 加入 a 的邻接表
nxs[b].push_back(a);
}
deque q;
// 1.找到入度为0的点
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
q.push_back(i);
}
}
// 2.迭代,更新入度
while (!q.empty()) {
int curr = q.front();
q.pop_front();
numCourses--; // 完成一个课程
for (int neighbor : nxs[curr]) {
if (--indegree[neighbor] == 0) {
q.push_back(neighbor);
}
}
}
// 如果所有课程都完成了,则返回 true
return numCourses == 0;
}
};
class Solution {
public:
vector findOrder(int numCourses, vector>& prerequisites) {
// 邻接表
vector> nxs (numCourses, vector());
// 入度数组
vector indegree (numCourses);
// 填充入度数组和邻接表
for (auto pre : prerequisites) {
int a = pre[0];
int b = pre[1];
indegree[a]++;
nxs[b].push_back(a);
}
vector res;
deque q;
int index = 0;
for (int i = 0; i < numCourses; ++i) {
if (indegree[i] == 0) {
q.push_back(i);
}
}
while (!q.empty()) {
int k = q.front();
q.pop_front();
res.push_back(k);
index++;
for (auto neg : nxs[k]) {
if (--indegree[neg] == 0) {
q.push_back(neg);
}
}
}
if (index != numCourses) {
return vector();
}else{
return res;
}
}
};
依次删去度数为 1 的点
class Solution {
public:
vector findMinHeightTrees(int n, vector>& edges) {
// 找到使得树的高度最小的节点
// 找到最中间的点
if (n == 1) return vector({0});
// 储存度数,而非入度
vector degrees(n, 0);
// 邻接表
vector> adjacencyList(n, vector());
for (auto edge : edges) {
int a = edge[0];
int b = edge[1];
degrees[a]++;
degrees[b]++;
adjacencyList[a].push_back(b);
adjacencyList[b].push_back(a);
}
// 队列,储存入度为 1
deque q;
// 找到度数为 1 的点
for (int i = 0; i < n; ++i) {
if (degrees[i] == 1) {
q.push_back(i);
}
}
vector res;
// 遍历所有边缘节点
while (!q.empty()) {
res.clear();
// 层序更新,这一批点处理完之后,先看结果对不对
int len = q.size();
for (int i = 0; i < len; ++i) {
int node = q.front();
q.pop_front();
res.push_back(node);
// 更新两个矩阵
for (auto nex : adjacencyList[node]) {
degrees[nex]--;
if (degrees[nex] == 1){
q.push_back(nex);
}
}
}
}
return res;
}
};
找到不能进入环的点,跟它在不在环里面没关系
有向图找环
从出度为 0 的点出发,它们不可能在环中
class Solution {
public:
vector eventualSafeNodes(vector>& graph) {
int n = graph.size();
// 出度
vector outdegree(n);
// 逆向邻接表
vector> pre_nodes(n, vector());
for (int i = 0; i < n; ++i) {
for (auto nx : graph[i]) {
// i --> nx
outdegree[i]++;
pre_nodes[nx].push_back(i);
}
}
deque q;
// 找到出度为 0 的点
for (int i = 0; i < n; ++i) {
if (outdegree[i] == 0) {
q.push_back(i);
}
}
// 储存结果
vector res;
while (!q.empty()) {
int node = q.front();
q.pop_front();
res.push_back(node);
for (auto nex : pre_nodes[node]) {
outdegree[nex]--;
if (outdegree[nex] == 0) {
q.push_back(nex);
}
}
}
sort(res.begin(), res.end());
return res;
}
};
查找连通块的数量
int[] fa;
void init(int n) {
fa = new int[n];// 初始化
for (int i = 0; i < n; ++i) fa[i] = i;// 遍历,都指向自己
}
// 0 和 3 是亲戚,则 0 和 3建立链接
int find(int x) {
// 如果 x 是自己的 boss 则返回 x
// 如果不是,则
return x == fa[x] ? x : (fa[x] = find(fa[x]));// 是否是根节点
}
void union(int x, int y) {
fa[find(x)] = find(y);// 连通
}
vector fa; // 并查集的父节点数组
// 初始化并查集,设置每个节点的父节点为自己
// 0 -- n-1
void init(int n) {
fa.resize(n);
for (int i = 0; i < n; ++i) {
fa[i] = i;
}
}
// 查找节点x所在的集合的根节点,同时进行路径压缩
int find(int x) {
return x == fa[x] ? x : (fa[x] = find(x));
}
// 合并两个节点所在的集合
void uni(int x, int y) {
fa[find(x)] = find(y);
}
构建并查集的操作基本都是一样的
1.查询根节点 + 路径压缩
2.合并块
题眼一般是多个集合的合并
并查集 + 求连通块的数量
class Solution {
public:
vector fa;
// 初始化 n 个城市的父节点为它们自己
void init(int n) {
fa.resize(n, 0);
for (int i = 0; i < n; ++i) {
fa[i] = i;
}
}
// 找 x 的夫节点
int find(int x) {
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
// 合并
void uni(int x, int y) {
fa[find(x)] = find(y);
}
// 判断多少个根节点
int findCircleNum(vector>& isConnected) {
int n = isConnected.size();
// 初始化
init(n);
for (int i = 0; i < n; ++i){
for (int j = i + 1; j < n; ++j) {
if (isConnected[i][j] == 1) uni(i, j);
}
}
// 检查最后有几个点的父节点是它自己,即根的数目
int cnt = 0;
for (int i = 0; i < n; ++i) {
if (fa[i] == i) {
cnt++;
}
}
return cnt;
}
};
根据父节点的特点找冗余路径
class Solution {
public:
vector fa;
// 节点是 1 -- n
void init(int n) {
fa.resize(n + 1, 0);
for (int i = 1; i <= n; ++i) {
fa[i] = i;
}
}
int find(int x) {
return fa[x] == x ? x : find(fa[x]);
}
void uni(int x, int y) {
fa[find(x)] = find(y);
}
vector findRedundantConnection(vector>& edges) {
int n = edges.size();
init(n);
for (auto edge : edges) {
int a = edge[0];
int b = edge[1];
// 如果父类节点都一样,那么找到了冗余路径
if (find(a) == find(b)) {
return edge;
} else {
uni(a, b);
}
}
return vector();
}
};
先连通,看连通块的数量,连接 n 个块需要 n - 1 个边
class Solution {
public:
vector fa;
void init(int n) {
fa.resize(n);
for (int i = 0; i < n; ++i) {
fa[i] = i;
}
}
int find(int x) {
return fa[x] == x ? x : (fa[x] = find(fa[x]));
}
void uni(int x, int y) {
fa[find(x)] = find(y);
}
int makeConnected(int n, vector>& connections) {
// 判断端点是否连通
// 如果已经连通可以拆除
// 连接 n 个连通块需要 n - 1 个边
init(n);
int cnt = 0;
for (auto con : connections) {
int a = con[0];
int b = con[1];
if (find(a) == find(b)) {
cnt++;
}else{
uni(a, b);
}
}
// 判断连通块的数量
int num = 0; // 初始化
for (int i = 0; i < n; ++i) {
if (fa[i] == i) {
num++;
}
}
// 判断边是不是够用
if (cnt >= num - 1) {
return num - 1;
}
return -1;
}
};
变体,需要维护连通块的数量
class Solution {
public:
// 需要维护每个连通块的数量
vector fa;
vector cnts;// 只对根节点生效
void init(int n) {
fa.resize(n);
cnts.resize(n);
for (int i = 0; i < n; ++i) {
fa[i] = i;
cnts[i] = 1;
}
}
int find(int x) {
return fa[x] == x ? x : (fa[x] = find(fa[x]));
}
void uni(int x, int y) {
int xp = fa[find(x)], yp = find(y);
fa[xp] = yp;
cnts[yp] += cnts[xp];
}
int getId(int x, int y, int col) {
return x * col + y;
}
vector pondSizes(vector>& land) {
// x * col + y
// 表示八个方向的方向数组
vector> dirs = {
{0, 1}, // 向右
{0, -1}, // 向左
{1, 0}, // 向下
{-1, 0}, // 向上
{1, 1}, // 右下
{1, -1}, // 右上
{-1, 1}, // 左下
{-1, -1} // 左上
};
int n = land.size();
int m = land[0].size();
init(n * m);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (land[i][j] == 0) {
for (auto dir : dirs) {
// 遍历八个方向
int nx = i + dir[0];
int ny = j + dir[1];
// 如果方向不越界 且 为水域
if (nx < 0 || ny < 0 || nx >= n || ny >= m || land[nx][ny] != 0) {
continue;
}
else{
int id1 = getId(i, j, m);
int id2 = getId(nx, ny, m);
if (find(id1) != find(id2)) {
uni(id1, id2);
}
}
}
}
}
}
vector res;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
int id = getId(i, j, m);
if (fa[id] == id && land[i][j] == 0) {
res.push_back(cnts[id]);
}
}
}
sort(res.begin(), res.end());
return res;
}
};
建立映射 [0, a, b] 其中 0 代表人名,a 代表邮箱地址
最后还要倒过来输出
#include
#include
#include