[Python] Union Find by Rank w Path Compression (Beats 99%)
First process the edges that both people can use. This has priority over Type 1 and Type 2 edges since the edge works for both Alice and Bob. After that, it\'s 
First process the edges that both people can use. This has priority over Type 1 and Type 2 edges since the edge works for both Alice and Bob. After that, it\'s textbook Union-Find for both Alice and Bob.\n\n```\nclass UnionFind:\n    """\n    Standard Union-Find data structure implementation\n    with path compression and rank\n    """\n    def __init__(self, n):\n        self.n = n\n        self.uf = [i for i in range(n + 1)]\n        self.rank = [0] * (n + 1)\n        self.comp = n\n    \n    def root(self, u):\n        if self.uf[u] != u:\n            self.uf[u] = self.root(self.uf[u])\n        return self.uf[u]\n\n    def union(self, u, v):\n        u, v = self.root(u), self.root(v)\n        if u == v: return False\n        if self.rank[u] > self.rank[v]:\n            self.uf[v] = u\n        elif self.rank[u] < self.rank[v]:\n            self.uf[u] = v\n        else:\n            self.uf[v] = u\n            self.rank[u] += 1\n        \n        self.comp -= 1\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # initialize both data structures to keep track of connected components\n        ufAlice = UnionFind(n)\n        ufBob = UnionFind(n)\n\n        numEdges = 0\n\n        # Add all type 3 edges that don\'t create a cycle (i.e. have the same parent in the \n        # data structure)\n        for t, u, v in edges:\n            if t == 3:\n                numEdges += int(ufAlice.union(u, v))\n                ufBob.union(u, v)\n        \n        # Unite all Alice / Bob edges that don\'t create a cycle\n        for t, u, v in edges:\n            if t == 1:\n                numEdges += int(ufAlice.union(u, v))\n            elif t == 2:\n                numEdges += int(ufBob.union(u, v))\n        \n        # if there are multiple components in either alice / bob\'s graphs, then it is \n        # disconnected and we return -1\n        if ufAlice.comp != 1 or ufBob.comp != 1:\n            return -1\n        \n        # otherwise, we can remove all edges that aren\'t chosen\n        return len(edges) - numEdges\n\n```\n\n**Analysis**\nTime: $O(E * \\alpha(N))$ where E is the number of edges. We traverse the edges array twice, and the time complexity of Union-Find is the *Inverse Ackermann Function* ($\\alpha(N)$), which is near-constant but can be treated as $\\log(n)$.\n\nSpace: $O(N)$ To Store the Union-Find data structures
['Python']