LeetCode题解系列--684. Redundant Connection

16 篇文章 0 订阅

描述

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


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值