您的位置:首页 > 其它

LeetCode题解系列--684. Redundant Connection

2017-10-10 14:36 441 查看

描述

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.

难度:medium

思路

这道题是一道关于无向图的题目,要求在给出的边中找出一条冗余的边,使得去掉这条边后,整个图仍然是连同的。若有多条边可供选择,则返回最后一条符合条件的边。

这题你如果不知道需要的算法,怕是要想很久。不过其实很简单,就是要用到一个叫做并查集的算法。

所谓并查集,其实是指两种对集合的操作,一种是并,一种是查。所谓并,就是将两个集合合并起来,所谓查就是检验两个元素是否在同一个集合中。

实现方式是这样,将所有元素放在一个森林中,同一个集合的元素便是在同一颗树上(不限制树的类型,多叉树)。

实际使用的数据结构,使用一个一维数组A,下标i为森林中的元素,A[i]存储的是其父节点,对于根节点,其父节点为其本身。初始化时为A[i]=i,即使得初始化时每个元素为一颗树。根据这个设定我们就可以得到查操作

int find(int x) {
if (set[x] != x) {
return find(set[x]);
}
return set[x];
}


对于并操作,只需要将一颗树的根节点随意接到另一颗树的任意一个节点,两个集合就完成了并操作。

void u(int x, int y) {
int xParent = find(x);
int yParent = find(y);
set[xParent] = yParent;
}


怎么样,很简单吧。但是这样的算法会造成树的高度越来越大,还可能造成一颗极不平衡的树,若其一直产生右偏树,则其效率基本与线性表相同。所以需要优化。

优化

前面我们有提到,我们对于树的形状没有要求,那么其实我们可以每增加一个节点都将起接到根节点,这样每次查询都会是常数级别的开销。即将树扁平化,这里就有一个很巧妙的方式来完成这个任务。

int find(int x) {
if (set[x] != x) {
set[x] = find(set[x]);
}
return set[x];
}


将刚刚使用的find函数进行改造,就可以在每次查询操作时都将树变得更加扁平,而每次并的操作都会调用find函数,所以每次都会将一部分节点直接接到根节点上,起到路径压缩的目的,减少树的层数。

答案

#include <vector>
using namespace std;
class Solution {
private:
vector<int> set;
public:
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
set = vector<int>(edges.size() + 1);
int N = edges.size();
for (int i = 1; i <= N; ++i) {
set[i] = i;
}
for (int i = 0; i < N; ++i) {
if (this->find(edges[i][0]) == this->find(edges[i][1])) {
return edges[i];
} else {
this->u(edges[i][0], edges[i][1]);
}
}
}
int find(int x) { if (set[x] != x) { set[x] = find(set[x]); } return set[x]; }
void u(int x, int y) { int xParent = find(x); int yParent = find(y); set[xParent] = yParent; }
};


点击这里查看更多我的LeetCode答案

参考资料:维基百科-并查集
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode