【LeetCode685】【并查集】每日一题day17

32 篇文章 0 订阅
14 篇文章 0 订阅

昨天day16做了一道二叉树的翻转,迭代或者递归都行,很简单没啥好说的。
今天这个并查集…我反省,我没看出来。
给你一棵有n个节点,n条边的树,让你找出那条多余的边。

考虑有两种情况:
1、冲突。即有一个点有两个父亲。这个可以统计每个点的入度,也可以在union的时候判断一下是不是已经有父节点了
2、成环。这里用到并查集,连接两个点的时候看看它们是不是已经连上了【这里应该能看出来是并查集的】

那么就可能出现三种情况:
1、只成环。删掉最后一条成环的边即可。
2、只冲突。删掉最后一条起冲突的边即可。
3、既冲突又成环,删掉有两个父节点的那个点成环的那条边。

有些思考难度,我觉得重点在于发现有冲突和成环两种情况。

class Solution {
public:
    int father[1005]; //全局变量

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

    void unionn(int u, int v)
    {
        //u = find(u);
        //v = find(v);
        father[v] = u;
    }

    int judge(int u, int v)
    {
        int x = find(u), y = find(v);
        if (x == y) return 0;
        if (y != v) return 1;
        return 2;
    }

    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        int size = edges.size();
        vector<int> ans;
        vector<int> colli;
        bool isColli = false;
        bool isLoop = false;
        for (int i = 0; i < size; i++)
        {
            father[edges[i][0]] = edges[i][0];
            father[edges[i][1]] = edges[i][1];
        }
        for (int i = 0; i < size; i++)
        {
            if (judge(edges[i][0], edges[i][1]) == 2)
            {
                unionn(edges[i][0], edges[i][1]);
                //printf("%d %d\n", edges[i][0], edges[i][1]);
            }
            else if (judge(edges[i][0], edges[i][1]) == 1)
            {
                colli = edges[i];
                isColli = true;
                //unionn(edges[i][0], edges[i][1]);
            }
            else 
            {
                ans = edges[i];
                isLoop = true;
            }
        }
        if (isColli == false) return ans;
        if (isLoop == false) return colli; 
        ans = {father[colli[1]], colli[1]};
        return ans;
    }
};

写起来不是很难,注意我的union函数以前是带路径压缩的【路径压缩也可以写在find函数里,让father[v] = find(father[v])】,但是在这道题里面因为它的边还是有用的,所以你不能改变它。
并查集还有一种优化方法是按秩合并,思想是维护每个节点作为根的那棵子树的高度,然后合并的时候总是把较矮的那棵树合并到较高的那棵树上,以后find的时候就会减少次数。这道题因为是有向边所以也不能用。不过我好像也从来没用过按秩合并,它相当于把迭代次数限定在logN以内,如果不这样的话查找可能是O(N)的,可能时间卡的比较紧,空间比较松的题就必须写了。

p.s. 今天上了第一节程序设计实验课,在课上把Ubuntu配置的差不多能用了嘿嘿 打算以后学习都用Ubuntu 不过还有好多要学的TvT 还遇见了很好的助教老师,感觉懂很多可以请教很多问题的样子。指针读到3/10了,了解了一点点系统栈的知识,之前递归和迭代有点不懂的现在竟然懂了hhh 马上就详写读书笔记!
昨天看了学姐的微博,说每天不刷题背书10小时以上就是白过,所以我也把每天学习10h以上作为一个小目标。现在已经尽量把一切空闲时间都用来学习了,不过提高效率还是能再挤出来两三个小时【想到了鲁迅hhh】
说起来鲁迅,最近对我有些影响的一件事是在图书馆偶然翻到了《管锥编》,发现每一页上我能认识的字不超过一半_(:з」∠)_ 人与人的差距真的比人和猪还大qnq 现在坚定信念觉得过去的事情已经过去了,但是未来确确实实还把握在我自己手里。也许…再学个几十年就能看懂《管锥编》了呢(x
好巧不巧,今天Momentum给的名言是说每天如果没有欢笑的话就是白过hhhhh感觉是和学姐有些不同的处世态度。可是学习10h是自己能控制的,欢笑不是呀。
话说最近听了几个很有意思的课,有点想把听课感想也写在csdn,不过和编程半毛钱关系没有嘿嘿

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值