code: def add_edge(self, u, v):
          
          # 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)
docs: O(log(n))