Ticket #14498: trac_14498_trees_classicals_algorithms_EliX-jbp.patch

File trac_14498_trees_classicals_algorithms_EliX-jbp.patch, 16.0 KB (added by elixyre, 6 years ago)
  • sage/combinat/abstract_tree.py

    # HG changeset patch
    # User Jean-Baptiste 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): 
    106106        sage: TestSuite(OrderedTree()).run()
    107107        sage: TestSuite(BinaryTree()).run()
    108108    """
     109   
     110    def pre_order_transversal( self, action =lambda node: None ):
     111        '''
     112        The depth first pre-order 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 pre-order 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[-i-1]
     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 post-order 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 post-order 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)
    109297
    110298    def subtrees(self):
    111299        """
  • 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 
    450450                    res.append(1-i)
    451451        add_leaf_rec(self)
    452452        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 infix-order 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 infixe-order 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    -right-rotate->  o     o
     572         / \                         /
     573        o   o                       o
     574
     575              __o__                         _o__
     576             /     \                       /    \
     577            o       o  -right-rotate->    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  -left-rotate->    o
     604             /                    / \
     605            o                    o   o
     606
     607              __o__                            o
     608             /     \                          /
     609            o       o  -left-rotate->        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]] )
    453629
    454630
    455631from sage.structure.parent import Parent
    class LabelledBinaryTree(AbstractLabelle 
    8791055            else:
    8801056                fils = self[1].binary_search_insert(letter)
    8811057                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 -right-rotate->  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 <-left-rotate- 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() )
    8821107
    8831108    _UnLabelled = BinaryTree
    8841109