O(log(n))

def add_edge(self, u, v):
        """
        O(log(n))
        """
        # print('add_edge u, v = %r, %r' % (u, v,))
        if self.graph.has_edge(u, v):
            return
        for node in (u, v):
            if not self.graph.has_node(node):
                self.graph.add_node(node)
            for Fi in self.forests:
                Fi.add_node(node)
        # First set the level of (u, v) to 0
        self.level[(u, v)] = 0
        # update the adjacency lists of u and v
        self.graph.add_edge(u, v)
        # If u and v are in separate trees in F_0, add e to F_0
        ru = self.forests[0].find_root(u)
        rv = self.forests[0].find_root(v)
        if ru is not rv:
            # If they are in different connected compoments merge compoments
            self.forests[0].add_edge(u, v)