def test_link_connectsTwoTrees(self): #arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') #act tree.link(a1,a2) #assert self.assertEqual(a1, tree.getRoot(a2))
def test_lca_query_order_doesnt_matter(self): #Arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') a3 = tree.makeTree('a3') tree.link(a1, a2) tree.link(a2, a3) self.assertEqual(tree.lca(a2, a3), tree.lca(a3, a2))
def test_lca_path_shouldReturnOlderNode(self): #Arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') a3 = tree.makeTree('a3') tree.link(a1, a2) tree.link(a2, a3) self.assertEqual(tree.lca(a2, a3), a2)
def test_lca_balancedTree_ShouldReturnRoot(self): #Arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') a3 = tree.makeTree('a3') tree.link(a1, a2) tree.link(a1, a3) self.assertEqual(tree.lca(a2, a3), a1)
def test_lca_query_order_doesnt_matter(self): #Arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') a3 = tree.makeTree('a3') tree.link(a1,a2) tree.link(a2,a3) self.assertEqual(tree.lca(a2,a3),tree.lca(a3,a2))
def test_getRoot__returnsProperRootWhenRootHasMultipleChildren(self): #arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') b1 = tree.makeTree('b1') tree.link(a1, a2) tree.link(a1, b1) #act / assert self.assertEqual(a1, tree.getRoot(a2)) self.assertEqual(a1, tree.getRoot(b1))
def test_lca_balancedTree_ShouldReturnRoot(self): #Arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') a3 = tree.makeTree('a3') tree.link(a1,a2) tree.link(a1,a3) self.assertEqual(tree.lca(a2,a3),a1)
def test_lca_path_shouldReturnOlderNode(self): #Arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') a3 = tree.makeTree('a3') tree.link(a1,a2) tree.link(a2,a3) self.assertEqual(tree.lca(a2,a3),a2)
def test_getRoot__returnsProperRootWhenRootHasMultipleChildren(self): #arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') b1 = tree.makeTree('b1') tree.link(a1,a2) tree.link(a1,b1) #act / assert self.assertEqual(a1, tree.getRoot(a2)) self.assertEqual(a1, tree.getRoot(b1))
def test_link_connectsTwoTrees(self): #arrange tree = LinkCutTree() a1 = tree.makeTree('a1') a2 = tree.makeTree('a2') #act tree.link(a1, a2) #assert self.assertEqual(a1, tree.getRoot(a2))
def test_getNode_nodeDoesntExist_returnsNone(self): tree = LinkCutTree() self.assertIsNone(tree.getNode('a1'))
def test_lca_multipleLinks_shouldFindLCA(self): tree = LinkCutTree() #Arrange 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) b1 = tree.makeTree('b1') b2 = tree.makeTree('b2') b3 = tree.makeTree('b3') tree.link(b1, b2) tree.link(b2, b3) tree.link(a3, b1) c1 = tree.makeTree('c1') c2 = tree.makeTree('c2') c3 = tree.makeTree('c3') tree.link(c1, c2) tree.link(c1, c3) tree.link(a5, c1) self.assertEqual(tree.lca(c3, b3), a3)
def test_makeRoot_path_shouldFlipPath(self): 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, 1) tree.link(a2, a3, 2) tree.link(a3, a4, 3) tree.link(a4, a5, 4) tree.link(a5, a6, 5) #act tree.makeRoot(a6) #assert self.assertEqual(a6, tree.getRoot(a1)) self.assertEqual(a6, a5.represented_parent) self.assertEqual(a5, a4.represented_parent) self.assertEqual(a4, a3.represented_parent) self.assertEqual(a3, a2.represented_parent) self.assertEqual(a2, a1.represented_parent)
def test_makeRoot_flip_tree(self): 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, 1) tree.link(a2, a3, 2) tree.link(a3, a4, 3) tree.link(a4, a5, 4) tree.link(a5, a6, 5) b1 = tree.makeTree('b1') b2 = tree.makeTree('b2') b3 = tree.makeTree('b3') tree.link(b1, b2) tree.link(b2, b3) tree.link(a3, b1) #act tree.makeRoot(b3) #assert self.assertEqual(b3, tree.getRoot(a6)) # nodes on the flipped path still share root self.assertEqual(tree.getRoot(b1), tree.getRoot(a2)) # node in flipped path and node out of path share root self.assertEqual(tree.getRoot(a6), tree.getRoot(a2)) self.assertEqual(b2, b1.represented_parent) self.assertEqual(b3, b2.represented_parent) self.assertEqual(b1, a3.represented_parent) self.assertEqual(a3, a2.represented_parent) self.assertEqual(a2, a1.represented_parent)
def test_link_threeTrees_getRootShouldBeSameForAll(self): tree = LinkCutTree() #Arrange 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) b1 = tree.makeTree('b1') b2 = tree.makeTree('b2') b3 = tree.makeTree('b3') tree.link(b1,b2) tree.link(b2,b3) tree.link(a3,b1) c1 = tree.makeTree('c1') c2 = tree.makeTree('c2') c3 = tree.makeTree('c3') tree.link(c1,c2) tree.link(c2,c3) tree.link(a5,c1) # act/assert self.assertEqual(a1, tree.getRoot(c1)) self.assertEqual(a1, tree.getRoot(b3))
def test_link__preservesRepresentedParent(self): ''' after linking all these tree's the represented_parent nodes should represent the paths in the represented tree ''' #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) tree.access(a3) c1 = tree.makeTree('c1') c2 = tree.makeTree('c2') c3 = tree.makeTree('c3') tree.link(c1,c2) tree.link(c2,c3) tree.link(a4,c1); # act/assert self.assertEqual(a1,a2.represented_parent) self.assertEqual(a2,a3.represented_parent) self.assertEqual(a3,a4.represented_parent) self.assertEqual(a4,a5.represented_parent) self.assertEqual(a5,a6.represented_parent) self.assertEqual(c1,c2.represented_parent) self.assertEqual(c2,c3.represented_parent) self.assertEqual(a4,c1.represented_parent)
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_makeRoot_flip_tree(self): 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,1) tree.link(a2,a3,2) tree.link(a3,a4,3) tree.link(a4,a5,4) tree.link(a5,a6,5) b1 = tree.makeTree('b1') b2 = tree.makeTree('b2') b3 = tree.makeTree('b3') tree.link(b1,b2) tree.link(b2,b3) tree.link(a3,b1) #act tree.makeRoot(b3) #assert self.assertEqual(b3, tree.getRoot(a6)) # nodes on the flipped path still share root self.assertEqual(tree.getRoot(b1), tree.getRoot(a2)) # node in flipped path and node out of path share root self.assertEqual(tree.getRoot(a6), tree.getRoot(a2)) self.assertEqual(b2,b1.represented_parent) self.assertEqual(b3,b2.represented_parent) self.assertEqual(b1,a3.represented_parent) self.assertEqual(a3,a2.represented_parent) self.assertEqual(a2,a1.represented_parent)
def test_lca_multipleLinks_shouldFindLCA(self): tree = LinkCutTree() #Arrange 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) b1 = tree.makeTree('b1') b2 = tree.makeTree('b2') b3 = tree.makeTree('b3') tree.link(b1,b2) tree.link(b2,b3) tree.link(a3,b1) c1 = tree.makeTree('c1') c2 = tree.makeTree('c2') c3 = tree.makeTree('c3') tree.link(c1,c2) tree.link(c1,c3) tree.link(a5,c1) self.assertEqual(tree.lca(c3,b3),a3)
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_makeRoot_path_shouldFlipPath(self): 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,1) tree.link(a2,a3,2) tree.link(a3,a4,3) tree.link(a4,a5,4) tree.link(a5,a6,5) #act tree.makeRoot(a6) #assert self.assertEqual(a6, tree.getRoot(a1)) self.assertEqual(a6, a5.represented_parent) self.assertEqual(a5, a4.represented_parent) self.assertEqual(a4, a3.represented_parent) self.assertEqual(a3, a2.represented_parent) self.assertEqual(a2, a1.represented_parent)
def test_link__preservesRepresentedParent(self): ''' after linking all these tree's the represented_parent nodes should represent the paths in the represented tree ''' #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) tree.access(a3) c1 = tree.makeTree('c1') c2 = tree.makeTree('c2') c3 = tree.makeTree('c3') tree.link(c1, c2) tree.link(c2, c3) tree.link(a4, c1) # act/assert self.assertEqual(a1, a2.represented_parent) self.assertEqual(a2, a3.represented_parent) self.assertEqual(a3, a4.represented_parent) self.assertEqual(a4, a5.represented_parent) self.assertEqual(a5, a6.represented_parent) self.assertEqual(c1, c2.represented_parent) self.assertEqual(c2, c3.represented_parent) self.assertEqual(a4, c1.represented_parent)
def test_getNode_nodeExists_returnsNode(self): tree = LinkCutTree() a1 = tree.makeTree('a1') self.assertEqual(a1, tree.getNode('a1'))
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
def __init__(self): self.forest = LinkCutTree() self.time = 0
def test_getNode_nodeExists_returnsNode(self): tree = LinkCutTree() a1 = tree.makeTree('a1') self.assertEqual(a1,tree.getNode('a1'))
def test_link_threeTrees_getRootShouldBeSameForAll(self): tree = LinkCutTree() #Arrange 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) b1 = tree.makeTree('b1') b2 = tree.makeTree('b2') b3 = tree.makeTree('b3') tree.link(b1, b2) tree.link(b2, b3) tree.link(a3, b1) c1 = tree.makeTree('c1') c2 = tree.makeTree('c2') c3 = tree.makeTree('c3') tree.link(c1, c2) tree.link(c2, c3) tree.link(a5, c1) # act/assert self.assertEqual(a1, tree.getRoot(c1)) self.assertEqual(a1, tree.getRoot(b3))