diff --git a/problems/0685.冗余连接II.md b/problems/0685.冗余连接II.md index 68d0376e..404813f3 100644 --- a/problems/0685.冗余连接II.md +++ b/problems/0685.冗余连接II.md @@ -39,7 +39,7 @@ **这说明题目中的图原本是是一棵树,只不过在不增加节点的情况下多加了一条边!** -还有**若有多个答案,返回最后出现在给定二维数组的答案。**这说明在两天边都可以删除的情况下,要删顺序靠后的! +还有**若有多个答案,返回最后出现在给定二维数组的答案。**这说明在两条边都可以删除的情况下,要删顺序靠后的! 那么有如下三种情况,前两种情况是出现入度为2的点,如图: @@ -58,7 +58,7 @@ 首先先计算节点的入度,代码如下: -```CPP +```cpp int inDegree[N] = {0}; // 记录节点入度 n = edges.size(); // 边的数量 for (int i = 0; i < n; i++) { @@ -70,7 +70,7 @@ for (int i = 0; i < n; i++) { 代码如下: -```CPP +```cpp vector vec; // 记录入度为2的边(如果有的话就两条边) // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案 for (int i = n - 1; i >= 0; i--) { @@ -112,7 +112,7 @@ vector getRemoveEdge(const vector>& edges) 本题C++代码如下:(详细注释了) -```CPP +```cpp class Solution { private: static const int N = 1010; // 如题:二维数组大小的在3到1000范围内 @@ -174,7 +174,7 @@ public: inDegree[edges[i][1]]++; // 统计入度 } vector vec; // 记录入度为2的边(如果有的话就两条边) - // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案 + // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案 for (int i = n - 1; i >= 0; i--) { if (inDegree[edges[i][1]] == 2) { vec.push_back(i); @@ -203,16 +203,313 @@ public: ## Java ```java + +class Solution { + + private static final int N = 1010; // 如题:二维数组大小的在3到1000范围内 + private int[] father; + public Solution() { + father = new int[N]; + + // 并查集初始化 + for (int i = 0; i < N; ++i) { + father[i] = i; + } + } + + // 并查集里寻根的过程 + private int find(int u) { + if(u == father[u]) { + return u; + } + father[u] = find(father[u]); + return father[u]; + } + + // 将v->u 这条边加入并查集 + private void join(int u, int v) { + u = find(u); + v = find(v); + if (u == v) return ; + father[v] = u; + } + + // 判断 u 和 v是否找到同一个根,本题用不上 + private Boolean same(int u, int v) { + u = find(u); + v = find(v); + return u == v; + } + + /** + * 初始化并查集 + */ + private void initFather() { + // 并查集初始化 + for (int i = 0; i < N; ++i) { + father[i] = i; + } + } + + /** + * 在有向图里找到删除的那条边,使其变成树 + * @param edges + * @return 要删除的边 + */ + private int[] getRemoveEdge(int[][] edges) { + initFather(); + for(int i = 0; i < edges.length; i++) { + if(same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边 + return edges[i]; + } + join(edges[i][0], edges[i][1]); + } + return null; + } + + /** + * 删一条边之后判断是不是树 + * @param edges + * @param deleteEdge 要删除的边 + * @return true: 是树, false: 不是树 + */ + private Boolean isTreeAfterRemoveEdge(int[][] edges, int deleteEdge) + { + initFather(); + for(int i = 0; i < edges.length; i++) + { + if(i == deleteEdge) continue; + if(same(edges[i][0], edges[i][1])) { // 构成有向环了,一定不是树 + return false; + } + join(edges[i][0], edges[i][1]); + } + return true; + } + + public int[] findRedundantDirectedConnection(int[][] edges) { + int[] inDegree = new int[N]; + for(int i = 0; i < edges.length; i++) + { + // 入度 + inDegree[ edges[i][1] ] += 1; + } + + // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案 + ArrayList twoDegree = new ArrayList(); + for(int i = edges.length - 1; i >= 0; i--) + { + if(inDegree[edges[i][1]] == 2) { + twoDegree.add(i); + } + } + + // 处理图中情况1 和 情况2 + // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树 + if(!twoDegree.isEmpty()) + { + if(isTreeAfterRemoveEdge(edges, twoDegree.get(0))) { + return edges[ twoDegree.get(0)]; + } + return edges[ twoDegree.get(1)]; + } + + // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了 + return getRemoveEdge(edges); + } +} ``` ## Python ```python + +class Solution: + + def __init__(self): + self.n = 1010 + self.father = [i for i in range(self.n)] + + + def find(self, u: int): + """ + 并查集里寻根的过程 + """ + if u == self.father[u]: + return u + self.father[u] = self.find(self.father[u]) + return self.father[u] + + def join(self, u: int, v: int): + """ + 将v->u 这条边加入并查集 + """ + u = self.find(u) + v = self.find(v) + if u == v : return + self.father[v] = u + pass + + + def same(self, u: int, v: int ): + """ + 判断 u 和 v是否找到同一个根,本题用不上 + """ + u = self.find(u) + v = self.find(v) + return u == v + + def init_father(self): + self.father = [i for i in range(self.n)] + pass + + def getRemoveEdge(self, edges: List[List[int]]) -> List[int]: + """ + 在有向图里找到删除的那条边,使其变成树 + """ + + self.init_father() + for i in range(len(edges)): + if self.same(edges[i][0], edges[i][1]): # 构成有向环了,就是要删除的边 + return edges[i] + self.join(edges[i][0], edges[i][1]); + return [] + + def isTreeAfterRemoveEdge(self, edges: List[List[int]], deleteEdge: int) -> bool: + """ + 删一条边之后判断是不是树 + """ + + self.init_father() + for i in range(len(edges)): + if i == deleteEdge: continue + if self.same(edges[i][0], edges[i][1]): # 构成有向环了,一定不是树 + return False + self.join(edges[i][0], edges[i][1]); + return True + + def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]: + inDegree = [0 for i in range(self.n)] + + for i in range(len(edges)): + inDegree[ edges[i][1] ] += 1 + + # 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案 + towDegree = [] + for i in range(len(edges))[::-1]: + if inDegree[edges[i][1]] == 2 : + towDegree.append(i) + + # 处理图中情况1 和 情况2 + # 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树 + if len(towDegree) > 0: + if(self.isTreeAfterRemoveEdge(edges, towDegree[0])) : + return edges[towDegree[0]] + return edges[towDegree[1]] + + # 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了 + return self.getRemoveEdge(edges) ``` ## Go ```go + +// 全局变量 +var ( + n = 1010// 节点数量3 到 1000 + father = make([]int, n) +) + +// 并查集初始化 +func initialize() { + for i := 0; i < n; i++ { + father[i] = i + } +} + +// 并查集里寻根的过程 +func find(u int) int { + if u == father[u] { + return u + } + father[u] = find(father[u]) + return father[u] +} + +// 将v->u 这条边加入并查集 +func join(u, v int) { + u = find(u) + v = find(v) + if u == v { + return + } + father[v] = u +} + +// 判断 u 和 v是否找到同一个根,本题用不上 +func same(u, v int) bool { + u = find(u) + v = find(v) + return u == v +} + +// getRemoveEdge 在有向图里找到删除的那条边,使其变成树 +func getRemoveEdge(edges [][]int) []int { + initialize() + for i := 0; i < len(edges); i++ { // 遍历所有的边 + if same(edges[i][0], edges[i][1]) { // 构成有向环了,就是要删除的边 + return edges[i] + } + join(edges[i][0], edges[i][1]) + } + return []int{} +} + +// isTreeAfterRemoveEdge 删一条边之后判断是不是树 +func isTreeAfterRemoveEdge(edges [][]int, deleteEdge int) bool { + initialize() + for i := 0; i < len(edges); i++ { + if i == deleteEdge { + continue + } + if same(edges[i][0], edges[i][1]) { // 构成有向环了,一定不是树 + return false + } + join(edges[i][0], edges[i][1]) + } + return true +} + +func findRedundantDirectedConnection(edges [][]int) []int { + inDegree := make([]int, len(father)) + for i := 0; i < len(edges); i++ { + // 统计入度 + inDegree[edges[i][1]] += 1 + } + // 记录入度为2的边(如果有的话就两条边) + // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案 + twoDegree := make([]int, 0) + for i := len(edges) - 1; i >= 0; i-- { + if inDegree[edges[i][1]] == 2 { + twoDegree = append(twoDegree, i) + } + } + + // 处理图中情况1 和 情况2 + // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树 + if len(twoDegree) > 0 { + if isTreeAfterRemoveEdge(edges, twoDegree[0]) { + return edges[twoDegree[0]] + } + return edges[twoDegree[1]] + } + + // 处理图中情况3 + // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了 + return getRemoveEdge(edges) +} + ``` ## JavaScript