685. Redundant Connection II

该博客介绍了如何利用并查集解决一类图论问题,即给定一个原本是根树的有向图,因额外添加了一条边变成了环状,要求找出这条冗余的边。作者详细解析了思路,包括如何判断环和冗余边,并给出了C++代码实现。最终,代码在测试中取得了很好的时间效率。
摘要由CSDN通过智能技术生成

685. Redundant Connection II

In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus every node has exactly one parent, except for the root node which has no parents.

The given input is a directed graph that started as a rooted tree with n nodes (with distinct values from 1 to n), with one additional directed 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 [ui, vi] that represents a directed edge connecting nodes ui and vi, where ui is a parent of child vi.

Return an edge that can be removed so that the resulting graph is a rooted tree of n nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array.

Example 1:

Input: edges = [[1,2],[1,3],[2,3]]
Output: [2,3]

Example 2:

Input: edges = [[1,2],[2,3],[3,4],[4,1],[1,5]]
Output: [4,1]

Constraints:

  • n == edges.length
  • 3 <= n <= 1000
  • edges[i].length == 2
  • 1 <= ui, vi <= n

解释:拿到题时开始没什么思路,一遍一遍读题,因为每个节点只有一个父结点,所以图可以看成一棵树,题目要求输出最后一条冗余的边,什么是冗余的边呢?

  1,要么是同一节点有两个父结点时,有一条冗余边

  2,要么是形成了回路,也就是环,肯定有条冗余边

因为题中说every node has exactly one parent, 因此想到可以用并查集来做,parent数组中parent[i]记录它的直接父结点。题中又说如果有多个答案的话返回最后的那个(暗示可以流水操作,不需要建立链接表)。有了大体思路,可以开始做题(列提纲)拉!

用vector<int>变量circle记录组成环形的最后一条边,first变量记录在一个节点有两个parent的情况下第一个parent,doubled记录第二个parent。

1,初始化parent[i] = i,每个节点都是自己的父结点,节点间互不联系

2,从头往后遍历输入数组的边,加入边时有几种情况:

              1)加入当前边后,图中组成了一个环,此时当前边不加入并查集,避免后续查询父结点时进入死循环。但当前边记录在circle变量中。

                     a) 如果此时已经出现一个节点有两个父结点的情况,则返回第一个父结点的边(因为在记录并查集时,第二个父结点并不记录进去,因此计算是否有环是使用第一个父结点计算的,因此第一个父结点是冗余边,例{{2,1},{3,1},{4,2},{1,4}},输出为{2,1})。

                     b)如果此时未出现两个父结点的情况,不能判断哪条边是冗余边,将最后组成环的边记录在circle中。以便最后输出时输出最后出现的一个。

              2)加入当前边后,当前边指向的节点已经有了父结点,即当前边构成了doubled节点。此时当前边不加入并查集(也可以加入并查集,但在circle计算时返回的值应相应的改为doubled而不是first了,其它地方也需要相应的调整),但记录在doubled变量里,同时将{parent[e[1]], e[1]}记录在first里,因为parent[e[1]]记录的是上一个父结点。

                     a) 如果此时已出现过环,则必为当前指向节点的前父结点和当前指向节点为冗余边,因为题目要求只有一条冗余边,而first, 与doubled出现两个父结点必有一条冗余边,而之前的环肯定是通过first计算出来的,因此first是冗余边。此时可以直接返回first。

                     b)如果没出现过环,只将first和doubled记录即可

              3)如果当前边加入后即无增加的环也无增加的double,只需将parent数组更新即可。parent[e[1]] = e[0],记录其直接父结点。

              4)有可能存在一条边加入后同时构成环和doubled,此时将边记录在circle中,因此最后返回时先返回circle,再返回doubled。如果只有circle或只有doubled的情况,则circle和doubled记录的都是最后出现的一条边,直接返回即可。

代码:

class Solution {
public:
    int find_parent(vector<int>& parent, int p){
    while(parent[p] != p)
        p = parent[p];
    return p;
}
vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        int n = edges.size();
        vector<int> parent(n+1, 0);
        for(int i=0; i<parent.size(); i++) parent[i] = i;
        vector<int> circle, first, doubled;
        for(auto e : edges){
            int x = find_parent(parent, e[0]);
            int y = find_parent(parent, e[1]);
            if(x == y) {
                if(doubled.size() > 0)
                    return first;
                circle = e;
            } else if(y != e[1]) {
                first = {parent[e[1]], e[1]};
                doubled = e;
                if(circle.size() > 0)
                    return first;
            } else parent[e[1]] = e[0];
        }
        if(circle.size() > 0) return circle;
        else return doubled;
    }
};

运行之后time打败了99.75,很是兴奋。

1. CLI - Command Line Interface 2. GUI - Graphical User Interface 3. SSH - Secure Shell 4. FTP - File Transfer Protocol 5. HTTP - Hypertext Transfer Protocol 6. TCP - Transmission Control Protocol 7. UDP - User Datagram Protocol 8. IP - Internet Protocol 9. DNS - Domain Name System 10. DHCP - Dynamic Host Configuration Protocol 11. NFS - Network File System 12. SMB - Server Message Block 13. RAID - Redundant Array of Independent Disks 14. LVM - Logical Volume Manager 15. RAID0 - Redundant Array of Independent Disks Level 0 16. RAID1 - Redundant Array of Independent Disks Level 1 17. RAID5 - Redundant Array of Independent Disks Level 5 18. RAID6 - Redundant Array of Independent Disks Level 6 19. RAID10 - Redundant Array of Independent Disks Level 10 20. ACL - Access Control List 21. SELinux - Security-Enhanced Linux 22. PAM - Pluggable Authentication Modules 23. SSHD - Secure Shell Daemon 24. SUDO - Superuser Do 25. CRON - Chronological Scheduler 26. IPTABLES - IP Tables 27. UFW - Uncomplicated Firewall 28. GRUB - Grand Unified Bootloader 29. LILO - Linux Loader 30. BIOS - Basic Input/Output System 31. UEFI - Unified Extensible Firmware Interface 32. GNOME - GNU Object Model Environment 33. KDE - K Desktop Environment 34. XFCE - XForms Common Environment 35. LXDE - Lightweight X11 Desktop Environment 36. ALSA - Advanced Linux Sound Architecture 37. PulseAudio - Pulse Audio 38. JACK - Jack Audio Connection Kit 39. GCC - GNU Compiler Collection 40. GDB - GNU Debugger 41. GNU - GNU's Not Unix 42. RPM - Red Hat Package Manager 43. DEB - Debian Package 44. YUM - Yellowdog Updater, Modified 45. APT - Advanced Packaging Tool 46. GIT - Global Information Tracker 47. SVN - Subversion 48. CVS - Concurrent Versions System 49. SSHFS - Secure Shell File System 50. NFS - Network File System.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值