LeetCode题解系列--684. Redundant Connection

来源:互联网 发布:剑三数据库 编辑:程序博客网 时间:2024/06/05 06:55

描述

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答案


参考资料:维基百科-并查集

原创粉丝点击