题目
In this problem, a tree is an undirected graph that is connected and has no cycles.
The given input is a graph that started as a tree with N nodes (with distinct values 1, 2, …, N), with one additional edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed.
The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] with u < v, that represents an undirected edge connecting nodes u and v.
Return an edge that can be removed so that the resulting graph is a tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array. The answer edge [u, v] should be in the same format, with u < v.
Example 1:
Input: [[1,2], [1,3], [2,3]]
Output: [2,3]
Explanation: The given undirected graph will be like this:
1
/ \
2 - 3
Example 2:
Input: [[1,2], [2,3], [3,4], [1,4], [1,5]]
Output: [1,4]
Explanation: The given undirected graph will be like this:
5 - 1 - 2
| |
4 - 3
Note:
The size of the input 2D-array will be between 3 and 1000.
Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array.
思路与解法
方法一
从题目中可知,我们需要从一个图中找出一条多余的边,使得去掉该边之后,剩余的节点和边形成一棵树(连通且无环)。题目保证输入数据有解,则我们可知输入数据为连通图,且一定至少存在一条边满足题目要求(该边与某些边形成了环)。
对于此题,我们仍然可以使用“剥洋葱”的思想:首先将度数为1的节点去掉,将与其相连节点的度数减1;之后,将当前图中度数为1的节点去掉,再将与其相连节点的度数减1……不断去掉度数为1的节点,直到没有度数为1的节点。此时,剩下的节点与边一定形成了环。最后,遍历该环上的每条边,找到最后输入的那条边返回即可。
代码实现
此算法我才用go语言实现:
func findRedundantConnection(edges [][]int) []int {
/*
* graph 存储图
* nodes_num 存储节点数,可有len(edges)计算得到
* degree 存储节点的度数
* visited 存储节点是否被删除,true表示已删除
* queue 利用队列实现BFS
*/
graph := make(map[int][]int)
nodes_num := len(edges)
degree := make([]int, nodes_num+1)
visited := make([]bool, nodes_num+1)
queue := make([]int ,0)
/* 存储graph,统计各节点度数 */
for _, edge :=range edges {
graph[edge[0]] = append(graph[edge[0]], edge[1])
graph[edge[1]] = append(graph[edge[1]], edge[0])
degree[edge[0]]++
degree[edge[1]]++
}
/* 将度数为1的节点加入到队列中,等待删除 */
for i:=1; i<=nodes_num; i++ {
if degree[i] == 1 {
queue = append(queue, i)
}
}
/* 直到寻找不到度数为1的节点,退出循环 */
for len(queue) > 0 {
head := queue[0]
queue = queue[1:]
visited[head] = true
for _, edge := range graph[head] {
degree[edge]--
if degree[edge] == 1 {
queue = append(queue, edge)
}
}
}
/* 从后向前遍历edges,返回满足visited[edges[i][0]] == false && visited[edges[i][1]] == false的边*/
for i:=nodes_num-1; i>=0 ;i-- {
if visited[edges[i][0]] == false && visited[edges[i][1]] == false {
return edges[i]
}
}
return nil
}
评测结果
方法二
我们可以采用并查集的思想:假如节点u
和v
之间存在一条边,则可以设置parent[u]=v
,即u
的父亲节点是v
,如果y
和u
也相连,则可以设置parent[y]=u
,即y
的父亲节点是u
;此时可以递归查询得到u
的父亲是v
(y
并没有父亲节点,其父亲即为自己),所以,可以设置parent[y]=v
,即将y
和v
直接相连。经过这样的处理,可以得知,一个连通分量的所有节点最终只存在一个父亲节点(此节点的父亲节点即为自身)。可以思考这样的情况:如果两个节点m
、n
经过边edge
相连,查找m
和n
的父亲节点相同,则表明m
、n
已经存在于同一个连通分量(m
到n
存在一条路径),则edge
即为查询的边。
代码实现
func findRedundantConnection(edges [][]int) []int {
/* parents存储各个节点的父亲节点 */
parents := make([]int ,len(edges)+1)
/* 查找一条边两个节点的父亲节点,并判断是否相等 */
for _, edge := range edges {
parent0 := findParent(edge[0], parents)
parent1 := findParent(edge[1], parents)
if parent0 == parent1 {
return edge
}
parents[parent0] = parent1
}
return []int{}
}
/* 递归查询node的父亲节点,如果不存在,则其父亲节点为自身 */
func findParent(node int, parents []int) int {
if parents[node] == 0 {
return node
}
return findParent(parents[node], parents)
}