LeetCode 685 - Redundant Connection II


https://leetcode.com/problems/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 1, 2, ..., 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 [u, v] that represents a directed edge connecting nodes u and v, where u is a parent of child v.
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: [[1,2], [1,3], [2,3]]
Output: [2,3]
Explanation: The given directed graph will be like this:
  1
 / \
v   v
2-->3
Example 2:
Input: [[1,2], [2,3], [3,4], [4,1], [1,5]]
Output: [4,1]
Explanation: The given directed graph will be like this:
5 <- 1 -> 2
     ^    |
     |    v
     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


  • http://www.cnblogs.com/grandyang/p/8445733.html
    这道题是之前那道Redundant Connection的拓展,那道题给的是无向图,只需要删掉组成环的最后一条边即可,归根到底就是检测环就行了。而这道题给我们的是有向图,那么整个就复杂多了,因为有多种情况存在,比如给的例子1就是无环,但是有入度为2的结点3。再比如例子2就是有环,但是没有入度为2的结点。其实还有一种情况例子没有给出,就是既有环,又有入度为2的结点。好,我们现在就来总结一下这三种情况:
    第一种:无环,但是有结点入度为2的结点(结点3)
    [[1,2], [1,3], [2,3]]
      1
     / \
    v   v
    2-->3

    第二种:有环,没有入度为2的结点
    [[1,2], [2,3], [3,4], [4,1], [1,5]]
    5 <- 1 -> 2
         ^    |
         |    v
         4 <- 3

    第三种:有环,且有入度为2的结点(结点1)
    [[1,2],[2,3],[3,1],[1,4]]
    复制代码
         4
        /
       v
       1
     /  ^
    v    \
    2 -->3
    复制代码

    对于这三种情况的处理方法各不相同,首先对于第一种情况,我们返回的产生入度为2的后加入的那条边[2, 3],而对于第二种情况,我们返回的是刚好组成环的最后加入的那条边[4, 1],最后对于第三种情况我们返回的是组成环,且组成入度为2的那条边[3, 1]。
    明白了这些,我们先来找入度为2的点,如果有的话,那么我们将当前产生入度为2的后加入的那条边标记为second,前一条边标记为first。然后我们来找环,为了方便起见,找环使用联合查找Union Find的方法,可参见Redundant Connection中的解法三。当我们找到了环之后,如果first不存在,说明是第二种情况,我们返回刚好组成环的最后加入的那条边。如果first存在,说明是第三种情况,我们返回first。如果没有环存在,说明是第一种情况,我们返回second

    X. Union Find
    https://zxi.mytechroad.com/blog/graph/leetcode-685-redundant-connection-ii/
    http://reeestart.me/2018/03/21/LeetCode-685-Redundant-Connection-II/
    http://hehejun.blogspot.com/2017/09/leetcoderedundant-connection-ii.html
    和之前题目不一样的是这道题目变成了有向图。那么变为有向图之后,我们对directed tree加上一条边和undirected tree有什么区别呢。可能会有以下三种状况:

    1. 仍然存在环,比如[1, 2], [2,3], [3,1]
    2. 不存在环但是存在某一个节点有两个父节点,比如[2,1], [3,1], [2, 3]
    3. 1和2的情况同时存在,比如[2,1], [3,1], [1,4], [4,2]
    所以和I的解法不同,这里每一种情况我们都要判断并删除相应的边,具体算法如下:
    1. 如果是情况1,根据题目的要求,我们删除边e。e满足条件,按照题目给定的边的顺序依次加上边,当加上e的时候,第一次出现环
    2. 如果是情况2,删除一条通向v的边。v满足条件其有两个父节点。e满足条件其在题目给定的边的顺序中比另外一条通向v的边出现较后
    3. 如果是情况3,删除一条通向v的边。v满足条件其有两个父节点。e满足条件其在环上
    我们只需要对这三种情况分别处理即可,DFS显然可以解,我们只需要记录环的路径(如果存在)和拥有两个父节点的v的对应的入射边(如果存在)。然后对于以上三种情况分别处理。但是DFS的解法需要我们建图。和I一样,我们可以用Union Find,这样省去了建图的步骤更为效率。在Union Find中我们也要对环和拥有两个父节点的v分别记录然后根据以上算法处理
        vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
            
            vector<int> parents(edges.size() + 1, 0);
            vector<int> roots(edges.size() + 1, 0);      
            vector<int> sizes(edges.size() + 1, 1);
            
            vector<int> ans1;
            vector<int> ans2;
            
            for(auto& edge: edges) {
                int u = edge[0];
                int v = edge[1];
                
                // A node has two parents
                if (parents[v] > 0) {
                    ans1 = {parents[v], v};
                    ans2 = edge;
                    
                    // Delete the later edge
                    edge[0] = edge[1] = -1;
                }
                
                parents[v] = u;
            }
            
            for(const auto& edge: edges) {
                int u = edge[0];
                int v = edge[1];
                
                // Invalid edge (we deleted in step 1)
                if (u < 0 || v < 0) continue;
                
                if (!roots[u]) roots[u] = u;
                if (!roots[v]) roots[v] = v;
                int pu = find(u, roots);
                int pv = find(v, roots);
                
                // Both u and v are already in the tree
                if (pu == pv)
                    return ans1.empty() ? edge : ans1;
                
                // Unoin, always merge smaller set (pv) to larger set (pu)
                if (sizes[pv] > sizes[pu])
                    swap(pu, pv);
                
                roots[pv] = pu;
                sizes[pu] += sizes[pv];
            }
            
            return ans2;
        }
        int find(int node, vector<int>& roots) {
            while (roots[node] != node) {
                roots[node] = roots[roots[node]];
                node = roots[node];
            }
            return node;
        }

    https://leetcode.com/problems/redundant-connection-ii/discuss/108045/C%2B%2BJava-Union-Find-with-explanation-O(n)
    This problem is very similar to "Redundant Connection". But the description on the parent/child relationships is much better clarified.
    There are two cases for the tree structure to be invalid.
    1) A node having two parents;
       including corner case: e.g. [[4,2],[1,5],[5,2],[5,3],[2,4]]
    2) A circle exists
    
    If we can remove exactly 1 edge to achieve the tree structure, a single node can have at most two parents. So my solution works in two steps.
    1) Check whether there is a node having two parents. 
        If so, store them as candidates A and B, and set the second edge invalid. 
    2) Perform normal union find. 
        If the tree is now valid 
               simply return candidate B
        else if candidates not existing 
               we find a circle, return current edge; 
        else 
               remove candidate A instead of B.

        public int[] findRedundantDirectedConnection(int[][] edges) {
            // can1 is before can2 and has higher priority
            int[] can1 = {-1, -1};
            int[] can2 = {-1, -1};
            int[] roots = new int[edges.length + 1];
            for (int[] edge : edges) {
                int father = edge[0];
                int child = edge[1];
                if (roots[child] == 0) {
                    roots[child] = father;
                } else if (roots[child] != 0) {
                    // the child already has father
                    // the newest link
                    can2 = new int[]{father, child};
                    // the older version of link
                    can1 = new int[]{roots[child], child};
                    // set the current link invalid
                    edge[1] = 0;
                }
            }
            
            // reuse the roots matrix, do union find
            for (int i = 0; i < roots.length; i++) {
                roots[i] = i;
            }
            
            for (int[] edge : edges) {
                int father = edge[0];
                int child = edge[1];
                if (child == 0) {
                    // current link is not valid
                    continue;
                } 
                if (find(roots, father) == child) {
                    // there is a cycle
                    if (can1[0] == -1) {
                        // candidate not exist
                        return edge;
                    } else {
                        // candidate exist
                        return can1;
                    }
                }
                // union
                roots[child] = father;
            }
            return can2;
        }
        
        
        private int find(int[] roots, int id) {
            while (id != roots[id]) {
                // path compression, optional
                roots[id] = roots[roots[id]];
                id = roots[id];
            }
            return id;
        }
    

    https://leetcode.com/articles/redundant-connection-ii/
    Starting from a rooted tree with N-1 edges and N vertices, let's enumerate the possibilities for the added "redundant" edge. If there is no loop, then either one vertex must have two parents (or no edge is redundant.) If there is a loop, then either one vertex has two parents, or every vertex has one parent.
    In the first two cases, there are only two candidates for deleting an edge, and we can try removing the last one and seeing if that works. In the last case, the last edge of the cycle can be removed: for example, when 1->2->3->4->1->5, we want the last edge (by order of occurrence) in the cycle 1->2->3->4->1 (but not necessarily 1->5).
    Algorithm
    We'll first construct the underlying graph, keeping track of edges coming from nodes with multiple parents. After, we either have 2 or 0 candidates.
    If there are no candidates, then every vertex has one parent, such as in the case 1->2->3->4->1->5. From any node, we walk towards it's parent until we revisit a node - then we must be inside the cycle, and any future seen nodes are part of that cycle. Now we take the last edge that occurs in the cycle.
    Otherwise, we'll see if the graph induced by parent is a rooted tree. We again take the root by walking from any node towards the parent until we can't, then we perform a depth-first search on this root. If we visit every node, then removing the last of the two edge candidates is acceptable, and we should. Otherwise, we should remove the first of the two edge candidates.
    In our solution, we use orbit to find the result upon walking from a node x towards it's parent repeatedly until you revisit a node or can't walk anymore. orbit(x).node (or orbit(x)[0] in Python) will be the resulting node, while orbit(x).seen (or orbit(x)[1]) will be all the nodes visited.
    • Time Complexity: O(N) where N is the number of vertices (and also the number of edges) in the graph. We perform a depth-first search.
    • Space Complexity: O(N), the size of the graph.
      public int[] findRedundantDirectedConnection(int[][] edges) {
        int N = edges.length;
        Map<Integer, Integer> parent = new HashMap();
        List<int[]> candidates = new ArrayList();
        for (int[] edge : edges) {
          if (parent.containsKey(edge[1])) {
            candidates.add(new int[] { parent.get(edge[1]), edge[1] });
            candidates.add(edge);
          } else {
            parent.put(edge[1], edge[0]);
          }
        }

        int root = orbit(1, parent).node;
        if (candidates.isEmpty()) {
          Set<Integer> cycle = orbit(root, parent).seen;
          int[] ans = new int[] { 0, 0 };
          for (int[] edge : edges) {
            if (cycle.contains(edge[0]) && cycle.contains(edge[1])) {
              ans = edge;
            }
          }
          return ans;
        }

        Map<Integer, List<Integer>> children = new HashMap();
        for (int v : parent.keySet()) {
          int pv = parent.get(v);
          if (!children.containsKey(pv))
            children.put(pv, new ArrayList<Integer>());
          children.get(pv).add(v);
        }

        boolean[] seen = new boolean[N + 1];
        seen[0] = true;
        Stack<Integer> stack = new Stack();
        stack.add(root);
        while (!stack.isEmpty()) {
          int node = stack.pop();
          if (!seen[node]) {
            seen[node] = true;
            if (children.containsKey(node)) {
              for (int c : children.get(node))
                stack.push(c);
            }
          }
        }
        for (boolean b : seen)
          if (!b)
            return candidates.get(0);
        return candidates.get(1);
      }

      public OrbitResult orbit(int node, Map<Integer, Integer> parent) {
        Set<Integer> seen = new HashSet();
        while (parent.containsKey(node) && !seen.contains(node)) {
          seen.add(node);
          node = parent.get(node);
        }
        return new OrbitResult(node, seen);
      }

    }

    class OrbitResult {
      int node;
      Set<Integer> seen;

      OrbitResult(int n, Set<Integer> s) {
        node = n;
        seen = s;
      }

    }


    https://leetcode.com/problems/redundant-connection-ii/discuss/108046/most-posted-answers-are-wrong

    Labels

    LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

    Popular Posts