题目描述:
在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。
输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1 到 n)的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 ui 是 vi 的一个父节点。
返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。
示例 1:

输入:edges = [[1,2],[1,3],[2,3]] 输出:[2,3]
示例 2:

输入:edges = [[1,2],[2,3],[3,4],[4,1],[1,5]] 输出:[4,1]
解题思路
- 计算每个节点的入度:首先遍历所有边,记录每个节点的入度。
- 检查是否存在入度为2的节点:如果存在入度为2的节点,说明有两个不同的边指向这个节点,这种情况下需要特殊处理。
- 处理入度为2的情况:
- 找出所有指向该节点的边。
- 尝试移除这两条边中的任意一条,检查剩余的边是否能构成一棵树。如果能,则返回被移除的那条边。
- 处理不存在入度为2的情况:
- 直接检查整个图是否有环,如果有环,则返回构成环的一条边。
代码实现
class Solution {
public:
vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
int n = edges.size();
vector<int> in_degree(n + 1, 0); // 记录每个点的入度
int two_degree_point = 0; // 记录入度为2的点
vector<vector<int>> two_degree_edge; // 记录入度为2对应的两条边
for (auto& edge : edges) {
int u = edge[0], v = edge[1];
in_degree[v]++;
if (in_degree[v] == 2) {
two_degree_point = v;
break;
}
}
// 如果出现了结果一
if (two_degree_point) {
for (int i = 0; i < n; ++i) {
if (edges[i][1] == two_degree_point) {
two_degree_edge.push_back(edges[i]);
}
}
reverse(two_degree_edge.begin(), two_degree_edge.end());
if (is_tree_after_remove_edge(edges, two_degree_edge[0])) {
return two_degree_edge[0];
} else {
return two_degree_edge[1];
}
}
// 如果出现了结果二
else {
return remove_circle_edge(edges);
}
}
private:
int find(int u, vector<int>& father) {
if (u != father[u]) {
father[u] = find(father[u], father);
}
return father[u];
}
bool is_same(int u, int v, vector<int>& father) {
return find(u, father) == find(v, father);
}
void join(int u, int v, vector<int>& father) {
u = find(u, father);
v = find(v, father);
if (u != v) {
father[v] = u;
}
}
bool is_tree_after_remove_edge(vector<vector<int>>& edges, vector<int>& target) {
int n = edges.size();
vector<int> father(n + 1);
for (int i = 0; i <= n; ++i) {
father[i] = i;
}
for (auto& edge : edges) {
if (edge == target) {
continue;
}
int u = edge[0], v = edge[1];
if (is_same(u, v, father)) { // 这表明u v已经连通,如果再加上(u, v)则冗余,故return False
return false;
} else {
join(u, v, father);
}
}
return true;
}
vector<int> remove_circle_edge(vector<vector<int>>& edges) {
int n = edges.size();
vector<int> father(n + 1);
for (int i = 0; i <= n; ++i) {
father[i] = i;
}
for (auto& edge : edges) {
int u = edge[0], v = edge[1];
if (is_same(u, v, father)) {
return edge;
} else {
join(u, v, father);
}
}
return {}; // 应该不会到这里,因为题目保证有且仅有一个环
}
};

被折叠的 条评论
为什么被折叠?



