def test_cut__leaves_bothTreesIntact(self):
        # arrange
        tree = LinkCutTree()
        a1 = tree.makeTree('a1')
        a2 = tree.makeTree('a2')
        a3 = tree.makeTree('a3')
        a4 = tree.makeTree('a4')
        a5 = tree.makeTree('a5')
        a6 = tree.makeTree('a6')
        tree.link(a1,a2)
        tree.link(a2,a3)
        tree.link(a3,a4)
        tree.link(a4,a5)
        tree.link(a5,a6)

        c1 = tree.makeTree('c1')
        c2 = tree.makeTree('c2')
        c3 = tree.makeTree('c3')
        tree.link(c1,c2)
        tree.link(c2,c3)

        tree.link(a6,c1);

        #act
        tree.cut(c2)

        #assert
        self.assertEqual(a1, tree.getRoot(c1))
        self.assertEqual(c2, tree.getRoot(c3))
    def test_cut__leaves_bothTreesIntact(self):
        # arrange
        tree = LinkCutTree()
        a1 = tree.makeTree('a1')
        a2 = tree.makeTree('a2')
        a3 = tree.makeTree('a3')
        a4 = tree.makeTree('a4')
        a5 = tree.makeTree('a5')
        a6 = tree.makeTree('a6')
        tree.link(a1, a2)
        tree.link(a2, a3)
        tree.link(a3, a4)
        tree.link(a4, a5)
        tree.link(a5, a6)

        c1 = tree.makeTree('c1')
        c2 = tree.makeTree('c2')
        c3 = tree.makeTree('c3')
        tree.link(c1, c2)
        tree.link(c2, c3)

        tree.link(a6, c1)

        #act
        tree.cut(c2)

        #assert
        self.assertEqual(a1, tree.getRoot(c1))
        self.assertEqual(c2, tree.getRoot(c3))
Exemple #3
0
class RetroactiveUnionFind(object):
    """Fully retroactive union find implemented using link-cut trees to represented disjoint forests"""
    def __init__(self):
        self.forest = LinkCutTree()
        self.time = 0

    # unionAgo(a,b) links nodes a and b in the LinkCutTree if they weren't already linked.
    # If they were it cuts the latest edge on the path between a and b (if it is later than the new link time).
    # The new subtree will contain exactly one of a and b. We make that node the root of the subtree and link it to
    # the other node still in the main tree.
    # This preserves old unions because any edge below the cut edge will now follow a path through the a-b edge up to
    # the lca, which is guaranteed not to have an edge value greater than the cut edge. Any edge above the cut \
    # edge will not be affected.
    def unionAgo(self, a_data, b_data, tdelta=0):
        # if the sets are already connected at the specified time return
        if self.sameSetAgo(a_data, b_data, tdelta):
            return

        #get node objects to work with
        a = self.forest.getNode(a_data)
        b = self.forest.getNode(b_data)
        union_time = self.time + tdelta
        if a is None:
            a = self.forest.makeTree(a_data)

        if b is None:
            b = self.forest.makeTree(b_data)

        # if the nodes are not connected at all, connect them. If they are connected at a later time,
        # cut the oldest edge on the path between the two nodes, make the union'ed node the root of that tree
        # and attach it to the other union'ed node.
        if self.forest.getRoot(a) != self.forest.getRoot(b):
            self.forest.makeRoot(b)
            self.forest.link(a, b, union_time)
        else:
            lca = self.forest.lca(a, b)
            max_time = float("-inf")
            max_time_node = None
            for next in [a, b]:
                while next is not lca:
                    if next.parent_edge_weight > max_time:
                        max_time = next.parent_edge_weight
                        max_time_node = next
                    next = next.represented_parent

            self.forest.cut(max_time_node)
            if self.forest.getRoot(a) == next:
                self.forest.makeRoot(a)
                self.forest.link(b, a, union_time)
            else:
                self.forest.makeRoot(b)
                self.forest.link(a, b, union_time)
        self.time += 1

    # sameSetAgo(a,b,t) will find the lca of a and b and traverse the path from both to the lca,
    # finding the largest edge on the path between a and b. If any edge is larger than time + tdelta then a, and b
    # were not connected at (time + tdelta)
    def sameSetAgo(self, a_data, b_data, tdelta=0):
        # sameset is reflexive
        if a_data == b_data:
            return True

        a = self.forest.getNode(a_data)
        b = self.forest.getNode(b_data)
        query_time = self.time + tdelta

        if a is None or b is None:
            return False

        lca = self.forest.lca(a, b)
        if lca is None:
            return False

        for next in [a, b]:
            while next is not lca:
                if next.parent_edge_weight > query_time:
                    return False
                next = next.represented_parent

        return True

    # sameSetWhen(a,b) traverses the path between a and b and return the largest edge,
    # which is the time at which a and b were connected.
    def sameSetWhen(self, a, b):
        lca = self.forest.lca(a, b)
        if lca is None:
            return float("-inf")

        max_time = float("-inf")
        for next in [a, b]:
            while next is not lca:
                if next.parent_edge_weight > max_time:
                    max_time = next.parent_edge_weight
                next = next.represented_parent

        return max_time