Ticket #14498: trac_14498_trees_classicals_algorithms_EliXjbp.patch
File trac_14498_trees_classicals_algorithms_EliXjbp.patch, 16.0 KB (added by , 6 years ago) 


sage/combinat/abstract_tree.py
# HG changeset patch # User JeanBaptiste Priez <jbp@kerios.fr> # Date 1367060516 7200 # Node ID edc0cfe0d5c55a0b0a989087ff87510b8dd31198 # Parent 45a6f6bb863b9a9948fe720b98f7aeb57c8ec56e several classical operations on trees and binary trees  on abstract trees: > depth pre/post order transversal algorithms > breadth first order transversal algorithm  on binary trees: > infix order transversal algorithm > canonical permutation associated to the left/right binary search tree insertion > left/right rotate (for labelled and unlabelled BT) diff git a/sage/combinat/abstract_tree.py b/sage/combinat/abstract_tree.py
a b class AbstractTree(object): 106 106 sage: TestSuite(OrderedTree()).run() 107 107 sage: TestSuite(BinaryTree()).run() 108 108 """ 109 110 def pre_order_transversal( self, action =lambda node: None ): 111 ''' 112 The depth first preorder transversal algorithm. 113 114 For example on the following binary tree `b`:: 115 116 ___3____ 117 / \ 118 1 _7_ 119 \ / \ 120 2 5 8 121 / \ 122 4 6 123 124 the ``depth first preorder transversal algorithm`` explores `b` in the 125 following order of nodes `3,1,2,7,5,4,6,8`. 126 127 The algorithm is:: 128 129 manipulate the root 130 then explore each subtrees (by the algorithm) 131 132 133 An other example:: 134 135 __1____ 136 / / / 137 2 6 8_ 138   / / 139 3_ 7 9 10 140 / / 141 4 5 142 143 The algorithm explorer this tree in the following order: 144 `1,2,3,4,5,6,7,8,9,10`. 145 146 TESTS:: 147 148 sage: l = [] 149 sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling(); b 150 3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]] 151 sage: b.pre_order_transversal(lambda node: l.append(node.label())) 152 sage: l 153 [3, 1, 2, 7, 5, 4, 6, 8] 154 sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling(); t 155 1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]] 156 sage: l = [] 157 sage: t.pre_order_transversal(lambda node: l.append(node.label())) 158 sage: l 159 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 160 sage: l = [] 161 sage: BinaryTree().canonical_labelling().pre_order_transversal(lambda node: l.append(node.label())) 162 sage: l 163 [] 164 sage: OrderedTree([]).canonical_labelling().pre_order_transversal(lambda node: l.append(node.label())) 165 sage: l 166 [1] 167 ''' 168 if self.is_empty(): return 169 stack = [] 170 stack.append( self ) 171 while len(stack) > 0: 172 node = stack.pop() 173 action( node ) 174 for i in range( len(node) ): 175 subtree = node[i1] 176 if not subtree.is_empty(): 177 stack.append( subtree ) 178 179 def post_order_transversal( self, action =lambda node: None ): 180 ''' 181 The depth first postorder transversal algorithm. 182 183 For example on the following binary tree `b`:: 184 185 ___3____ 186 / \ 187 1 _7_ 188 \ / \ 189 2 5 8 190 / \ 191 4 6 192 193 the ``depth first postorder transversal algorithm`` explores `b` in the 194 following order of nodes `2,1,4,6,5,8,7,3`. 195 196 The algorithm is:: 197 198 explore each subtrees (by the algorithm) 199 then manipulate the root 200 201 An other example:: 202 203 __1____ 204 / / / 205 2 6 8_ 206   / / 207 3_ 7 9 10 208 / / 209 4 5 210 211 The algorithm explorer this tree in the following order: 212 `4,5,3,2,7,6,9,10,8,1`. 213 214 TESTS:: 215 216 sage: l = [] 217 sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling(); b 218 3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]] 219 sage: b.post_order_transversal(lambda node: l.append(node.label())) 220 sage: l 221 [2, 1, 4, 6, 5, 8, 7, 3] 222 sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling(); t 223 1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]] 224 sage: l = [] 225 sage: t.post_order_transversal(lambda node: l.append(node.label())) 226 sage: l 227 [4, 5, 3, 2, 7, 6, 9, 10, 8, 1] 228 sage: l = [] 229 sage: BinaryTree().canonical_labelling().post_order_transversal(lambda node: l.append(node.label())) 230 sage: l 231 [] 232 sage: OrderedTree([]).canonical_labelling().post_order_transversal(lambda node: l.append(node.label())) 233 sage: l 234 [1] 235 ''' 236 if self.is_empty(): return 237 for subtree in self: 238 subtree.post_order_transversal( action ) 239 action( self ) 240 241 242 def breadth_first_order_transversal(self, action = lambda node: None): 243 ''' 244 The breadth first order transversal algorithm. 245 246 For example on the following binary tree `b`:: 247 248 ___3____ 249 / \ 250 1 _7_ 251 \ / \ 252 2 5 8 253 / \ 254 4 6 255 256 the ``breadth first order transversal algorithm`` explores `b` in the 257 following order of nodes `3,1,7,2,5,8,4,6`. 258 259 The algorithm is:: 260 261 queue < ( root ) 262 while the queue is not empty: 263 node < pop( queue ) 264 manipulate the node 265 append in the queue all subtrees of the node 266 267 TESTS:: 268 269 sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling() 270 sage: l = [] 271 sage: b.breadth_first_order_transversal(lambda node: l.append(node.label())) 272 sage: l 273 [3, 1, 7, 2, 5, 8, 4, 6] 274 sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling(); t 275 1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]] 276 sage: l = [] 277 sage: t.breadth_first_order_transversal(lambda node: l.append(node.label())) 278 sage: l 279 [1, 2, 6, 8, 3, 7, 9, 10, 4, 5] 280 sage: l = [] 281 sage: BinaryTree().canonical_labelling().breadth_first_order_transversal(lambda node: l.append(node.label())) 282 sage: l 283 [] 284 sage: OrderedTree([]).canonical_labelling().breadth_first_order_transversal(lambda node: l.append(node.label())) 285 sage: l 286 [1] 287 ''' 288 if self.is_empty(): return 289 queue = [] 290 queue.append(self) 291 while len(queue) > 0: 292 node = queue.pop() 293 action(node) 294 for subtree in node: 295 if not subtree.is_empty(): 296 queue.insert(0, subtree) 109 297 110 298 def subtrees(self): 111 299 """ 
sage/combinat/binary_tree.py
diff git a/sage/combinat/binary_tree.py b/sage/combinat/binary_tree.py
a b class BinaryTree(AbstractClonableTree, C 450 450 res.append(1i) 451 451 add_leaf_rec(self) 452 452 return res[1:1] 453 454 def in_order_transversal(self, 455 node_action = lambda node: None, 456 leaf_action = lambda leaf: None 457 ): 458 ''' 459 The depth first infixorder transversal algorithm. 460 461 For example on the following binary tree `b` where we denote leafs by 462 `a,b,c...` and nodes by `1,2,3...`:: 463 464 ____3____ 465 / \ 466 1 __7__ 467 / \ / \ 468 a 2 _5_ 8 469 / \ / \ / \ 470 b c 4 6 h i 471 / \ / \ 472 d e f g 473 474 the ``depth first infixeorder transversal algorithm`` explores `b` in 475 the following order of nodes `a,1,b,2,c,3,d,4,e,5,f,6,g,7,h,8,i`. 476 477 The algorithm is:: 478 479 explore the left subtree 480 manipulate the root 481 explore the right subtree 482 483 TESTS:: 484 485 sage: nb_leaf = 0 486 sage: def l_action(_): 487 ....: global nb_leaf 488 ....: nb_leaf += 1 489 sage: nb_node = 0 490 sage: def n_action(_): 491 ....: global nb_node 492 ....: nb_node += 1 493 sage: BinaryTree().in_order_transversal(n_action, l_action) 494 sage: nb_leaf, nb_node 495 (1, 0) 496 sage: nb_leaf, nb_node = 0, 0 497 sage: b = BinaryTree([[],[[],[]]]); b 498 [[., .], [[., .], [., .]]] 499 sage: b.in_order_transversal(n_action, l_action) 500 sage: nb_leaf, nb_node 501 (6, 5) 502 sage: nb_leaf, nb_node = 0, 0 503 sage: b = b.canonical_labelling() 504 sage: b.in_order_transversal(n_action, l_action) 505 sage: nb_leaf, nb_node 506 (6, 5) 507 sage: l = [] 508 sage: b.in_order_transversal(lambda node: l.append( node.label() )) 509 [1, 2, 3, 4, 5] 510 sage: leaf = 'a' 511 sage: l = [] 512 sage: def l_action(_): 513 ....: global leaf, l 514 ....: l.append(leaf) 515 ....: leaf = chr( ord(leaf)+1 ) 516 sage: n_action = lambda node: l.append( node.label() ) 517 sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling() 518 sage: b.in_order_transversal(n_action, l_action) 519 sage: l 520 ['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 6, 'g', 7, 'h', 8, 'i'] 521 ''' 522 if self.is_empty(): 523 leaf_action( self ) 524 return 525 self[0].in_order_transversal(node_action, leaf_action) 526 node_action( self ) 527 self[1].in_order_transversal(node_action, leaf_action) 528 529 def canonical_permutation(self, left_to_right = True): 530 ''' 531 Compute the canonical permutation associated to the binary search tree 532 insertion from the `right` or the `left` of the permutation. 533 534 TESTS:: 535 536 sage: b = BinaryTree([[[],[]],[[],None]]) 537 sage: b.canonical_permutation(False) 538 [1, 3, 2, 5, 6, 4] 539 sage: b.canonical_permutation(True) 540 [4, 2, 1, 3, 6, 5] 541 sage: b.canonical_permutation() 542 [4, 2, 1, 3, 6, 5] 543 sage: b.canonical_permutation().binary_search_tree().shape() == b 544 True 545 sage: b.canonical_permutation(False).binary_search_tree(False).shape() == b 546 True 547 sage: b.canonical_permutation(False).binary_search_tree().shape() == b 548 False 549 sage: b.canonical_permutation().binary_search_tree(False).shape() == b 550 False 551 ''' 552 from sage.combinat.permutation import Permutation 553 assert( isinstance(left_to_right, bool) ) 554 l = [] 555 if left_to_right: 556 self.canonical_labelling().pre_order_transversal( 557 lambda node : l.append( node.label() ) 558 ) 559 else: 560 self.canonical_labelling().post_order_transversal( 561 lambda node : l.append( node.label() ) 562 ) 563 return Permutation( l ) 564 565 def right_rotate( self ): 566 ''' 567 Right rotation operation of tree: 568 569 o _o_ 570 / / \ 571 o rightrotate> o o 572 / \ / 573 o o o 574 575 __o__ _o__ 576 / \ / \ 577 o o rightrotate> o _o_ 578 / \ / / \ 579 o o o o o 580 / \ \ 581 o o o 582 583 TESTS:: 584 585 sage: b = BinaryTree([[[],[]], None]); b 586 [[[., .], [., .]], .] 587 sage: b.right_rotate() 588 [[., .], [[., .], .]] 589 sage: b = BinaryTree([[[[],None],[None,[]]], []]);b 590 [[[., .], .], [., [., .]]], [., .]] 591 sage: b.right_rotate() 592 [[[., .], .], [[., [., .]], [., .]]] 593 ''' 594 B = self.parent()._element_constructor_ 595 return B( [self[0][0], B( [self[0][1], self[1]] )] ) 596 597 def left_rotate( self ): 598 ''' 599 Right rotation operation of tree: 600 601 _o_ o 602 / \ / 603 o o leftrotate> o 604 / / \ 605 o o o 606 607 __o__ o 608 / \ / 609 o o leftrotate> o 610 / \ / 611 o o o 612 / \ / \ 613 o o o o 614 / \ 615 o o 616 617 618 TESTS:: 619 620 sage: b = BinaryTree([[],[[],None]]); b 621 [[., .], [[., .], .]] 622 sage: b.left_rotate() 623 [[[., .], [., .]], .] 624 sage: b.left_rotate().right_rotate() == b 625 True 626 ''' 627 B = self.parent()._element_constructor_ 628 return B( [B( [self[0], self[1][0]] ), self[1][1]] ) 453 629 454 630 455 631 from sage.structure.parent import Parent … … class LabelledBinaryTree(AbstractLabelle 879 1055 else: 880 1056 fils = self[1].binary_search_insert(letter) 881 1057 return LT([self[0], fils], label=self.label()) 1058 1059 def right_rotate(self): 1060 ''' 1061 Right rotation operation of tree: 1062 1063 y x 1064 / \ / \ 1065 x C rightrotate> A y 1066 / \ / \ 1067 A B B C 1068 1069 TESTS:: 1070 1071 sage: LB = LabelledBinaryTree 1072 sage: b = LB([LB([LB([],"A"), LB([],"B")],"x"),LB([],"C")], "y"); b 1073 y[x[A[., .], B[., .]], C[., .]] 1074 sage: b.right_rotate() 1075 x[A[., .], y[B[., .], C[., .]]] 1076 ''' 1077 B = self.parent()._element_constructor_ 1078 return B( [ 1079 self[0][0], 1080 B( [self[0][1], self[1]], self.label() ) 1081 ], self[0].label() ) 1082 1083 def left_rotate( self ): 1084 ''' 1085 Right rotation operation of tree: 1086 1087 y x 1088 / \ / \ 1089 x C <leftrotate A y 1090 / \ / \ 1091 A B B C 1092 1093 1094 TESTS:: 1095 1096 sage: LB = LabelledBinaryTree 1097 sage: b = LB([LB([LB([],"A"), LB([],"B")],"x"),LB([],"C")], "y"); b 1098 y[x[A[., .], B[., .]], C[., .]] 1099 sage: b == b.right_rotate().left_rotate() 1100 True 1101 ''' 1102 B = self.parent()._element_constructor_ 1103 return B( [ 1104 B([self[0], self[1][0]], self.label()), 1105 self[1][1] 1106 ], self[1].label() ) 882 1107 883 1108 _UnLabelled = BinaryTree 884 1109