题目描述:
给定一棵n各节点(节点值1~n)的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。
原题参考链接:https://leetcode-cn.com/problems/7LpjUW
分析:
简而言之,本题默认给出的是包含一个单独闭环的图,只需要按照输入edges的逆序定位到首次出现在闭环上的边,即可将图还原为树。
示例1:
1 输入: edges = [[1,2],[1,3],[2,3]] 2 输出: [2,3]
示例2:
1 输入: edges = [[1,2],[2,3],[3,4],[1,4],[1,5]] 2 输出: [1,4]
Cpp解法1-递归除边:
search_one_pair 函数配合 vector<bool>edgeVisited 来寻找未被访问的路径与其节点;Cut_pair函数根据unordered_map<int,int>M来对每个节点的Edge计数,对M[Node]==1的Node调用search_one_pair查找配对Node_target,此过程更新M;再对M[Node_target]递归使用Cut_pair方法;去除的是非环边:
1 class Solution { 2 private: 3 vector<bool>edgeVisited; 4 unordered_map<int,int>M; 5 int search_one_pair(vector<vector<int>>& edges,int key){ 6 for(int i=0;i<edges.size();i++){ 7 if(edgeVisited[i]==true)continue; 8 if(edges[i][0]==key){ 9 edgeVisited[i]=true; 10 return edges[i][1]; 11 } 12 if(edges[i][1]==key){ 13 edgeVisited[i]=true; 14 return edges[i][0]; 15 } 16 } 17 return -1; 18 } 19 void Cut_pair(vector<vector<int>>& edges){ 20 auto q=M.begin(); 21 while(q!=M.end()){ 22 if(q->second==1){ 23 q->second=0; 24 int temp=search_one_pair(edges,q->first); 25 M[temp]--; 26 //cout<<q->first<<"*"<<q->second<<" "; 27 if(M[temp])Cut_pair(edges);//Cut again or not 28 } 29 q++; 30 } 31 return; 32 } 33 public: 34 vector<int> findRedundantConnection(vector<vector<int>>& edges) { 35 edgeVisited.resize(edges.size(),false); 36 for(int i=0;i<edges.size();i++){ 37 M[edges[i][0]]++; 38 M[edges[i][1]]++; 39 } 40 Cut_pair(edges); 41 for(int i=edges.size()-1;i>=0;i--){ 42 if(M[edges[i][0]]>0 && M[edges[i][1]]>0){ 43 //cout<<"^"<<M[edges[i][0]]<<" "<<M[edges[i][1]]<<"^"; 44 return edges[i]; 45 } 46 } 47 return {}; 48 } 49 };
时间复杂度:O(n^2)
空间复杂度:O(n)
注意:
unordered_map相较于其他存储结构有利于降低时间复杂度,否则会达到时间复杂度上限。
Cpp解法2-并查集:
分析:
对于任何一棵有n个节点的树只可能有n-1条边,任何闭环上的边均同时属于其两个节点所在的子图,利用此条件可以快速按照egde顺序来合并各子树,最后返回的边就是最后出现在edge中的闭环边;
注意:初始化节点为单独子树,初始vector<int>FatherIndex长度为节点个数n=egdes.size()-1。
对示例1:
1 class Solution { 2 private: 3 int findFatherNode(vector<int>& FatherIndex,int point){ 4 if(FatherIndex[point]==point)return point; 5 FatherIndex[point]=findFatherNode(FatherIndex,FatherIndex[point]); 6 return FatherIndex[point]; 7 } 8 public: 9 vector<int> findRedundantConnection(vector<vector<int>>& edges) { 10 vector<int>FatherIndex(edges.size()+1); 11 for(int i=1;i<edges.size();i++){ 12 FatherIndex[i]=i;//initialize n subtrees 13 } 14 for(int i=0;i<edges.size();i++){ 15 int Fres1=findFatherNode(FatherIndex,edges[i][0]); 16 int Fres2=findFatherNode(FatherIndex,edges[i][1]); 17 if(Fres1==Fres2){ 18 return edges[i];//last edge 19 }else{ 20 FatherIndex[Fres2]=Fres1; 21 } 22 } 23 return {}; 24 } 25 };
时间复杂度:O(n)
空间复杂度:O(n)