题目链接 https://leetcode-cn.com/problems/redundant-connection/
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着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
注意:
思路:逐个去掉某一个边,测试其是否存在环。
解法1,使用邻接矩阵,超内存。所以我把链接矩阵改成bool形式,连通使用1表示,否则使用0,(原来链接矩阵使用的是INT_MAX),勉强通过!
class Solution {
public:
bool flag=true;bool INF=0;
vector findRedundantConnection(vector>& edges) {
const int maxn=1000;
vector temp(maxn,INF);
vector< vector > graph(maxn,temp);
int mn=0;
int n;
for(int i=0;i-1;i--)
{
int a=edges[i][0]-1;
int b=edges[i][1]-1;
graph[a][b]=INF;
graph[b][a]=INF;
//print(graph,n);
flag=true;
dfs(graph);
if(!flag) ;//cout<<"have"< >&graph,int n)
{
for(int i=0;i >&graph, int node, vector&visit,
vector&father)
{
if(!flag) return;
int n = graph.size();
visit[node] = 1;
//cout<";
// tmp = father[tmp];
//}
//cout< >&graph)
{
int n = graph.size();
vector visit(n, 0); //visit°´ÕÕËã·¨µ¼ÂÛ22.3½Ú·ÖΪÈýÖÖ״̬
vector father(n, -1);// father[i] ¼Ç¼±éÀú¹ý³ÌÖÐiµÄ¸¸½Úµã
for(int i = 0; i < n; i++)
if(visit[i] == 0&&flag)
dfsVisit(graph, i, visit, father);
}
};
解法2:邻接表:
class Solution {
public:
bool flag=true;
int INF=-1;//INT_MAX;
vector findRedundantConnection(vector>& edges) {
int mn=0;
int n;
for(int i=0; i > G(n); //临界表
for(int i=0; i-1; i--) {
int a=edges[i][0]-1;
int b=edges[i][1]-1;
int posa,posb;
for(int j=0; j >&graph,int n) {
for(int i=0; i >&graph, int node, vector&visit,
vector&father) {
if(!flag) return;
int n = graph.size();
visit[node] = 1;
//cout<";
tmp = father[tmp];
}
cout< >&graph) {
int n = graph.size();
vector visit(n, 0); //visit°′????·¨μ???22.3?ú·??aèy??×′ì?
vector father(n, -1);// father[i] ????±éàú1y3ì?Diμ????úμ?
for(int i = 0; i < n; i++)
if(visit[i] == 0&&flag)
dfsVisit(graph, i, visit, father);
};
};
解法3 通过并查集
class Solution {
public static int[] fa;
public int find(int x){
return fa[x]==x?x:(fa[x]=find(fa[x]));
}
public static int[] res;
public void merge(int a,int b){
int x=find(a);
int y=find(b);
if(x!=y){
fa[x]=y;
}else{
res=new int[]{a,b};
}
}
public int[] findRedundantConnection(int[][] edges) {
if(edges==null||edges[0].length==0){
return new int[2];
}
res=null;
int n=edges.length;
fa=new int[n+1];
for(int i=1;i<=n;++i){
fa[i]=i;
}
for(int i=0;i
总结:
一、无向图回路的判断
对于无向图,判断其是否有回路有四种方法,如下所示:
1、利用深度优先搜索DFS,在搜索过程中判断是否会出现后向边(DFS中,连接顶点u到它的某一祖先顶点v的边),即在DFS对顶点进行着色过程中,若出现所指向的顶点为黑色,则此顶点是一个已经遍历过的顶点(祖先),出现了后向边,若完成DFS后,则图中有回路;
2、在图的邻接表表示中,首先统计每个顶点的度,然后重复寻找一个度为1的顶点,将度为1和0的顶点从图中删除,并将与该顶点相关联的顶点的度减1,然后继续反复寻找度为1的,在寻找过程中若出现若干顶点的度都为2 sanzhangpai,则这些顶点组成了一个回路;否则,图中不存在回路。
3、利用BFS,在遍历过程中,为每个节点标记一个深度deep,如果存在某个节点为v,除了其父节点u外,还存在与v相邻的节点w使得deep[v]<=deep[w]的,那么该图一定存在回路;
4、用BFS或DFS遍历,最后判断对于每一个连通分量当中,如果边数m>=节点个数n,那么改图一定存在回路。因此在DFS或BFS中,我们可以统计每一个连通分量的顶点数目n和边数m,如果m>=n则return false;直到访问完所有的节点,return true。
二、有向图回路的判断
对于有向图,判断其是否有回路的方法也有两种,如下所示:
1、与无向图类似,若在DFS中出现后向边,即存在某一顶点被第二次访问到,则有回路出现;
2、同样,利用拓扑排序的思想,通过这一步骤来执行拓扑排序,即重复寻找一个入度为0的顶点,将该顶点从图中删除,并将该顶点及其所有的出边从图中删除(即与该点相应的顶点的入度减1),最终若途中全为入度为1的点,则这些点至少组成一个回路。
对于有向图,具体点就可得到如下分析:
问题分析:如果图中存在回路,则必包含一个子图为回路。即该子图中所有顶点入度不为0且至少有边指向另外的顶点。
算法:
步骤1:按邻接表方式存储图。遍历与每个节点关联的边并统计每个节点的入度。需要O(n+m)次的运算。
步骤2:删除所有入度为零的顶点及其相发出的边。并将被删除边所指向的顶点的入度减1。
重复步骤2直到:
(1)所有顶点被删除(则没有回路)或;
(2)还有顶顶点但没有入度为零的顶点可删除(则存在回路)。
算法复杂度分析:
由于步骤二中的删除边的操作运算复杂度为O(m),删除节点的操作为O(n) 判断节点入度是否为0需要O(n+m)次运算。其中O(n)次为初始入度为零的节点,O(m)次为删除边后导致的入度为零的节点。于是整个算法复杂度为O(m+n)。