数据结构与算法Python语言实现《Data Structures & Algorithms in Python》手写课后答案--第十一章

本文主要探讨了使用Python实现的第十一章平衡树相关算法,包括相关构造性能问题的分析和习题代码展示,部分代码来源于《Data Structures & Algorithms in Python》一书。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第十一章

本章叙述了不同平衡树的构造性能问题

习题代码如下(部分代码引用书中源代码,源代码位置目录在第二章答案中介绍)
#11.1
#(1,A)
#   \
#   (2,B)
#       \
#       (3,C)
#           \
#           (4,D)
#               \
#               (5,E)

#11.2   详细图略
#                   30
#                 /    \
#               24      40
#             /   \       \
#           11    26      58
#             \           /
#              13       48

#11.3  四种
#       /     /     \     \
#      /      \     /      \

#11.4   (1,2,3,4) 和 (3,2,1,4)

#11.5   (3,4,2,1) 和 (1,2,3,4)

#11.6
def text6(self,p,k):
    while p.key()!=k:
        if p.key()>k:                   # search the left subtree
            if self.left(p) is None:
                break
            p=self.left(p)
        elif p.key()<k:                 # search the right subtree
            if self.right(p) is None:
                break
            p=self.right(p)
    return p

#11.7 图11-12是自旋转两次  图11-14是自旋转一次

#11.8
#                 62
#            /          \
#         50              78
#       /     \             \
#     44       54            88
#   /   \     /
# 17    48  52

#11.9
#                 54
#            /          \
#         44              78
#       /     \             \
#     17       50            88
#             /  \
#           48    54

#11.10 在一颗用数组方式存储的二叉树中,这棵树的任意一个节点的位置通过父节点在数组中的索引计算得到的
#      如果一个父节点需要交换位置,那么特的孩子节点也需要交换到其他的位置

#11.11   用(T,D)表示每个节点、T:树的名称   D:树的深度
#删除前:
#                       (z,h+2)
#                   /          \
#           (t1,h)              (y,h+1)
#                               /     \
#                          (t2,h)        (x,h)    
#                                       /   \
#                                 (t3,h-1) (t4,h-1)
#删除后:z出现不平衡
#                       (z,h+2)
#                    /         \
#           (t1,h-1)            (y,h+1)
#                               /     \
#                          (t2,h)        (x,h)    
#                                       /   \
#                                 (t3,h-1) (t4,h-1)

#旋转:
#                          (y,h+2)
#                         /       \
#                   (z,h+1)        (x,h)
#                  /    \          /     \
#           (t1,h-1)  (t2,h)  (t3,h-1) (t4,h-1)

#11.12  
#删除前:
#                       (z,h+2)
#                   /          \
#           (t1,h)              (y,h+1)
#                               /     \
#                           (x,h)    (t4,h-1)
#                           /   \
#                     (t2,h-1) (t3,h-1)
#删除后:z出现不平衡
#                       (z,h+2)
#                   /          \
#           (t1,h-1)            (y,h+1)       
#                               /     \
#                          (x,h)      (t4,h-1)
#                         /   \
#                 (t2,h-1)      (t3,h-1)
#第一次旋转:
#                       (z,h+2)
#                     /        \
#           (t1,h-1)            (x,h+1)
#                              /        \
#                         (t2,h-1)      (y,h)
#                                      /     \
#                             (t3,h-1)       (t4,h-1)
#第二次旋转:
#                          (x,h+1)
#                         /       \
#                   (z,h)           (y,h)
#                  /    \          /     \
#           (t1,h-1)  (t2,h-1) (t3,h-1) (t4,h-1)

#11.13 在trindoe时,直接出现不平衡
#删除前:
#                       (z,h+2)
#                   /          \
#           (t1,h)              (y,h+1)
#                               /     \
#                          (x,h)        (t4,h)    
#                          /   \
#                    (t2,h-1) (t3,h-1)
#删除后:
#                       (z,h+2)
#                   /          \
#           (t1,h-1)            (y,h+1)
#                               /     \
#                          (x,h)        (t4,h)    
#                          /   \
#                    (t2,h-1) (t3,h-1)
#第一次旋转:在x处出现不平衡
#                       (z,h+2)
#                   /          \
#           (t1,h-1)            (x,h+2)
#                              /     \
#                         (t2,h-1)     (y,h+1)
#                                     /     \
#                             (t3,h-1)       (t4,h)

#11.14    只给出最终图形
#a:
#                           18
#                          /
#                       16
#                      /
#                   ···
#                  /
#                 0
#b:                 
#                           18
#                          /
#                       16
#                      /
#                   ···
#                  /
#                 0
#                  \
#                    2
#c:略

#11.15  不知如何描述问题的答案,上了chegg网查询
#查询出的答案是关于splay树的存储,会让最大的元组在树的根部,其次每个元素都在父节点的左部(但是access有存取也有访问的意思)
#如果是顺序访问的话根据上一题画的图,大部分节点也会在其父节点的左孩子节点上

#11.16 不是(2,4)树需要满足两个条件:A 每个节点最多只能有四个自节点,最少两个自节点;B 每个外部节点的深度一样

#11.17 将k2存入w的父节点中
# (k1,k2,k3,k4) 需要将其分为两部分,第一部分一个节点,第二部分两个节点;
# 因为需要一个比第一部分大,又比第二部分小的值.所以只有k2,k3 能够满足大小关系
# 因为第一个部分一个节点,第二部分两个节点.所以需要将k2放入w中.

#11.18
#{1,2,3,4,5}
#               2
#              / \
#             1   (3,4,5)
#{5,4,3,2,1}
#               4
#              / \
#       (1,2,3)   5

#11.19  当存在两个或者两个以上个3-node节点,就可以画出多个不同的红黑树对应相同的(2,4)树

#11.20
#   1):每个节点都是4-node
#                          (4,8,12 )
#                       /     |  |     \
#                (1,2,3)(5,6,7)(9,10,11)(13,14,15)
#   2):每个节点都是2-node   构成完全二叉树   略

#11.21
#   1):                     ( 5 , 16 , 22 )
#                       /       |    |        \
#                 (1,2)     (10,12) (18)      (30,45,50)
#   2): (N,C)  N: 键值 C: 颜色
#                               (16,b)
#                             /        \
#                        (5,r)          (22,r)
#                       /     \        /      \
#                   (2,b)   (10,b) (18,b)      (45,b)
#                  /             \            /      \
#               (1,r)             (12,r)  (30,r)      (50,r)

#11.22
#   a):F,红黑树的任意以黑色节点为根节点的子树为红黑树
#   b):T,当一个节点没有兄弟节点时,他的父节点只有一个节点,需要满足(只有一个子节点、没有子节点)的黑色深度相同
#       也就是说这个节点要和父节点有相同的黑色深度,所以这个节点必须为红色节点.
#   c):T:多棵红黑树对应一颗(2,4)树,取决于红黑树中3-node节点个个数,(2,4)树中的一个3-node节点对应两种红黑的形式.
#   d):F:c中已解释

#11.23
#   三种树结构,是对旋转的不同使用结果;在旋转结束后,每个节点映射到水平线上位置不发生改变;
#   中序遍历是从左向右进行遍历,所以不会改变中序遍历结果

#11.24
#   1): 100000  #按大小顺序插入
#   2): log(100000)
#   3): 100000  #按大小顺序插入
#   4): log(100000)
#   5): log(100000)

#11.25    (N,C,H)   N:为键值 C:为颜色(b,r)  H:为深度
#               (4,b,4)
#              /     \
#         (3,b,1)     (7,r,3)
#                    /     \
#               (5,b,2)       (8,b,1)
#                    \
#                     (6,r,1)

#11.26 在红黑树中,一个节点A如果只有一个叶子节点,那么这个叶子节点和节点A是要有相同的黑色高度的
#       所以叶子节点应该为红色,A应该为黑色

#11.27 在删除之前,p有两个叶子节点a,b;为了保证两个叶子节点黑色深度,当a是红色时,b一定时红色,当b时黑色时,a一定时黑色;
#      在删除之后,p有一个叶子节点,所以如果被保留的叶子节点是黑色时,就会因为需要节点p和它被保留的黑色子节点深度相同,破坏黑色深度

#11.28 在删除之前,p有两个叶子节点a,b,并且b有一个左子节点c,
#       此时保证了a,b,c三节点的黑色深度相同,就需要c为红色,所以a,b为黑色
#      删除b后,p还是拥有两个子节点

#11.29  红黑树和AVL树的高度都保持在log(n),所以直接将序列逐个插入就可以实现
#       O(h*2^(h-1)的求和,1<=h<=log(n) )=O(n*(logn-1/2)-1)

#11.30  可以      伸展树的摊销运行时间为O(logn) 即使在最坏的情况下,搜索一个元素的时间需要O(n),但是摊销后还是log(n)

#11.31  将__setitem__方法修改即可
from TheCode.ch11.binary_search_tree import TreeMap
class BST1(TreeMap):
    def setdefaut(self,k,v):
        ''' return k's value if k exist in the tree,else add (k,d)'''
        if self.is_empty():
            leaf = self._add_root(self._Item(k, v))     # from LinkedBinaryTree
        else:
            p = self._subtree_search(self.root(), k)
            if p.key() == k:
                return p.element()._value
            else:
                item = self._Item(k, v)
                if p.key() < k:
                    # inherited from LinkedBinaryTree
                    leaf = self._add_right(p, item)
                else:
                    # inherited from LinkedBinaryTree
                    leaf = self._add_left(p, item)
        # hook for balanced tree subclasses
        try:     # add try grammar for text the ADT
            self._rebalance_insert(leaf)
        except e:
            pass
        
#t=BST1()
#for i in range(5):
#    t.setdefaut(i,i)
#print(t.setdefaut(i,i+10))

#11.32
#树的旋转:
#           |                           |
#           y                           x
#          / \          <==>           / \
#        x    t3                    t1    y
#       / \                              / \
#    t1    t2                          t2   t3
#在树中,通过每两个节点之间的位置关系,使二叉树有不同的形状,但都可以通过旋转的方式改变树的整体结构

#11.33
#当我们寻找一个节点的最大的lt时 A:如果这个节点有孩子节点,我们找到左孩子的最右节点,
#                            如果没有孩子节点,向上寻找父节点,知道找到一个小于自己的父节点
#当我们寻找一个节点的最小的gt时 B:如果这个节点有孩子节点,我们找到右孩子的最左节点,
#                            如果没有孩子节点,向上寻找父节点,知道找到一个大于自己的父节点
#当我们没有找到k时,可以将k看成没有孩子节点的情况,所以他的A、和B都在他的父节点上

#11.34
#   after()时间复杂度保证在O(h)内,在最坏的情况下从树的根节点遍历到树的叶子节点;
#   但在find_range()方法中最坏的情况只发生一次,其余时间复杂度度为O(1)

#11.35
#   提要:在删除根节点或者只有一个孩子的节点时,可以直接删除,不需要寻找其他节点替换
#   将需要删除的节点分为两部分,start(第一个节点)和another(除第一个节点以外其余节点)
#   A:在another中的第一个节点一定为整棵树的某个叶子节点或只有一个孩子的节点(大于start的最小节点)
#   B:another中的第二个节点为A中找到节点的父节点(如果A中找到的是叶子节点)或者A中找到节点的右节点的最左节点(如果A只有一个孩子的节点,回到A)
#   C:another中的第三个节点为A中找到节点的父节点的右节点(如果A中找到的是叶子节点),等等
#   在理想情况下:A和C为B的左右节点,先删除A和C然后在删除B,向上递归删除,
#   非理想情况下:也是从叶子结点开始向上删除(还是按照从下向上删除节点)
#   最后可能会存最上面的节点有左右孩子,所以需要使用小的最大的节点来替换这个节点来删除
#   助理root节点

#11.36
#   在AVL树中,每删除一个节点都需要向上查找是否出现不平衡,所以时间复杂度为O(slogN)

#11.37
#在每个节点加入以该节点为根的所有自节点个数
#还要修改对应的增加和删除,每次操作后都需要更新祖父节点中记录的值
#以下是查找算法
#Algorithm(star,stop):
#   find first node:    start<=node's<=stop
#   s=sum of node's child num
#   big=node'right                  {right node mark the max num}
#   small=node'left                 {left node mark the min mum}
#   repeat:                         {cut down the branch which beyond the range}
#       if big!=None:              
#           if big<=stop:
#               big=big.right
#           else:
#               sum-=sum of big's right child num and 1
#               big=big.right
#       if small!=None:
#           if small>=start:
#               small=small.left
#           else:
#               sum-=sum of small's left child num and 1
#               small=small.right   
#   untill: big and small are None
#   return sum

#11.38  需要修改_Node中存储子节点的个数
# 在_relink方法中修改

#11.39  分为三部分,前那两部分构成严格意义的log(n)的时间
#   part1:寻找节点k             {k深度的时间}
#   part2:寻找比k节点小的最大节点 {从k的深度开始向下,直到树的根部}
#   part3:被删除的节点可能会造成树高度不平衡,需要trinode   {可能为1可能为log(n)}
#ps:trinode为rotation之后树的高度有所减小,树变得更广泛;rotation是一个只改变位置不改变高的的操作

#11.40
# 1.平衡因子通过计算获得
# 2.根据父节点平衡因子的正负决定左旋还是右旋
# 3.在rotate方法中,重新计算旋转改变的平衡因子,在_rebalance中计算改变增加节点改变的平衡因子
from TheCode.ch11.avl_tree import AVLTreeMap
class FactorAVL(AVLTreeMap):
    '''
        alter variable:
            _height
        alter method:
            left_height
            right_height
            _recompute_height
            _isbalanced
            _tall_child
            _rebalance
            _rotate
    '''
    class _Node(AVLTreeMap._Node):
        __slots__ = '_factor'         # additional data member to store height

        def __init__(self, element, parent=None, left=None, right=None):
            super().__init__(element, parent, left, right)
            self._factor = 0            # will be recomputed during balancing
            
        def left_factor(self):
            return self._left._factor if self._left is not None else 0

        def right_factor(self):
            return self._right._factor if self._right is not None else 0

    #------------------------- positional-based utility methods --------------
    def _recompute_factor(self, p,orient):
        ''' have aid of the orient parameter'''
        if orient==None:
            return 
        p._node._factor =p._node._factor+1 if orient=='l' else p._node._factor-1

    def _isbalanced(self, p):
        return p._node._factor!=2 or p._node._factor!=-2    # return False when factor== 2 or -2

    def _tall_child(self, p, favorleft=False):  # parameter controls tiebreaker
        if p._node.left_factor() + (1 if favorleft else 0) > p._node.right_factor():
            return self.left(p)
        else:
            return self.right(p)

    def _tall_grandchild(self, p):
        child = self._tall_child(p)             # 子孩子一定有一个节点高
        # if child is on left, favor left grandchild; else favor right
        # grandchild
        alignment = (child == self.left(p))
        return self._tall_child(child, alignment)

    def _rebalance(self, p):
        orient=None                     # definition the orient of child,'l' if leftchild else 'r'
        while p is not None:
            old_height = p._node._height                          # trivially 0 if new node        
            self._recompute_factor(p,orient)
            # imbalance detected!
            if not self._isbalanced(p):
                # perform trinode restructuring, setting p to resulting root,
                # and recompute new local heights after the restructuring
                p = self._restructure(self._tall_grandchild(p))
            # adjust for recent changes            
            if p._node._height == old_height and orient==None:      # has height changed?
                p = None                                            # no further changes needed
            else:
                # cheak the orients
                if p is self.parent(p).left():
                    orient='l'
                else:
                    orient='r'
                # repeat with parent
                p = self.parent(p)
                
    def _rotate(self, p):
        """Rotate Position p above its parent.

        Switches between these configurations, depending on whether p==a or p==b.

              y                  y
             / \                /  \
            x  t2             t0   x
           / \                     / \
          t0  t1                  t1  t2

        Caller should ensure that p is not the root.
        """
        """Rotate Position p above its parent."""
        x = p._node
        y = x._parent                                 # we assume this exists
        # grandparent (possibly None)
        z = y._parent
        if z is None:
            self._root = x                              # x becomes root
            x._parent = None
        else:
            # x becomes a direct child of z
            self._relink(z, x, y == z._left)
        # now rotate x and y, including transfer of middle subtree
        if x == y._left:
            # x._right becomes left child of y
            self._relink(y, x._right, True)
            # y becomes right child of x
            self._relink(x, y, False)
            #calculate the factor
            y._factor=y._factor+min(-x._factor,0)+1
            x._factor=x._factor+min(0,y._factor)-1
        else:
            # x._left becomes right child of y
            self._relink(y, x._left, False)
            # y becomes left child of x
            self._relink(x, y, True)
            #calculate the factor
            y._factor=y._factor+max(-y._factor,0)+1
            x._factor=x._factor+max(0,y._factor)+1
            
#text11.40
#t=FactorAVL()
#for i in range(20):
#    t[i]=i
#for i in range(20):
#    print(t[i])
    
#11.41   围绕树的增、删、改、查,对函数进行更改
#增:通过对比新增的节点大小关系,来更新最小值的指针
#删:通过判断删除的节点是否为指针指向节点,来更新指针,若为最小值,则进行中序遍历
#改:修改只会更改键值对应的内容,所以此部分不需要修改
#查:将与获取树最小节点有关的方法使用find_min代替

#11.42 AVL树不需要进行更改
from TheCode.ch11.avl_tree import AVLTreeMap
class LeftAVL(AVLTreeMap):
    '''
        add a filed to store the minium node,it's a position class;
        control the minimum node when add node or delete node
    '''
    #-----------------------------add a filed----------------------------------
    def __init__(self):
        super().__init__()
        self._mininum=None               # a Position class
    #-----------------------------control the mininum----------------------------------
    def _add_root(self,k,v):
        temp=super()._add_root(k,v)
        if k==None or k<self._mininum.element()._key:
            self._mininum=temp
        return temp
            
    def _add_left(self,k,v):
        temp=super()._add_left(k,v)
        if k==None or k<self._mininum.element()._key:
            self._mininum=temp
        return temp
    
    def _add_right(self,k,v):
        temp=super()._add_right(k,v)
        if k==None or k<self._mininum.element()._key:
            self._mininum=temp
        return temp

    '''def _rebalance_insert(self,p):
        if self._mininum==None or p.element()._key<self._mininum.element()._key:
            self._mininum=p
        super()._rebalance_insert(p)'''
    
    def __delitem__(self,k):
        ''' remove the node which values equal k'''
        if k==self._mininum.element()._key:
            self._muninum=self.after(self._mininum)
        super().__delitem__(k)
        
    def _subtree_first_position(self, p):
        """Return Position of first item in subtree rooted at p."""
        return self._mininum

#text11.42    
#t=LeftAVL()
#for i in range(20,10,-1):
#    t[i]=i
#    #print(t._mininum)
#    print(t.first().element()._key)
#for i in range(11,21):
#    del t[i]
#    print('del',i)
#    print(t.first().element()._key)

#11.43 直接在AVLTreeMap树上使用
from TheCode.ch11.avl_tree import AVLTreeMap
class AFAVL(AVLTreeMap):
    '''
        add a field to make after and before method worst-cost O(1)-time;
    '''
    #-----------------------------add a filed----------------------------------

    class _Node(AVLTreeMap._Node):
        __slots__ = '_before','_after'         # additional data member to store height

        def __init__(self, element, parent=None, left=None, right=None,before=None,after=None):
            super().__init__(element, parent, left, right)
            self._before=before
            self._after=after
    #-----------------------------alter add method----------------------------------    
    def _add_left(self,p,e):
        '''  inherit from linked_binary_tree'''
        temp=super()._add_left(p,e)
        node = self._validate(temp)
        p=node._parent                      # get the parent node
        node._after=p                       # tie the node
        node._before=p._before
        if p._before !=None:                # if p is the mininum value node
            node._before._after=node
        node._after._before=node
        return temp

    def _add_right(self,p,e):
        '''  inherit from linked_binary_tree'''
        temp=super()._add_right(p,e)
        node = self._validate(temp)
        p=node._parent                      # get the parent node
        node._before=p                      # tie the node
        node._after=p._after
        if p._after != None:                 # if p is the maxnum value node
            node._after._before=node
        node._before._after=node
        return temp
    
    #-----------------------------alter delete method----------------------------------    

    def _delete(self,p):
        '''  inherit from linked_binary_tree'''
        temp=super()._delete(p)
        temp._after._before=temp._before    # tie the node
        temp._before._after=temp._after

    def after(self,p):
        node=self._validate(p)                   # inherited from LinkedBinaryTree
        return self._make_position(node._after)

    def before(self,p):
        node=self._validate(p)
        return self._make_position(node._before)
    
#text11.43
    
#t=AFAVL()
#for i in range(20):
#    t[i]=i
#    print(i)
#temp=t.first()
#for i in range(19):
#    temp=t.after(temp)
#    print(temp)
#print('#####')
#for i in range(20):
#    temp=t.before(temp)
#    print(temp)
    
#11.44 如上提代码,无需修改

#11.45 delete(p)方法需要找到比p小的最大节点,在找节点的过程中消耗了O(h),在修改过后,可以在O(1)的时间内找到该节点

#11.46 新增两个field存储当前节点左右自节点个数,通过计算节点数来得到索引
from TheCode.ch11.binary_search_tree import TreeMap
class SupplementTM(TreeMap):
    '''
        add two field 
    '''
    #-----------------------------alter two field----------------------------------    

    class _Node(TreeMap._Node):
        ''' inherit from linked_binary_tree '''
        __slots__='_left_num','_right_num'
        def __init__(self,element, parent=None, left=None, right=None,_left_num=0,_right_num=0):
            super().__init__(element, parent, left, right)
            self._right_num=_right_num              # add field for sum of right child num
            self._left_num=_left_num                # add field for sum of left child num

    class Position(TreeMap.Position):
        ''' inherit from linked_binary_tree
            add two methods for coding conveniently
        '''
        def left_num(self):
            return self._node._left_num             # return 
        def right_num(self): 
            return self._node._right_num

    def _change_num(self,temp,num):
        ''' temp is the beginning node which is a Position class,
            num is the int where need to change;
            node's num  on the route which from node p to root add num
        '''
        p=self.parent(temp)
        while p is not None:    # cycle to add the child num
            if temp is self.left(p):            # if p is left child ,parent._left_num+=1
                p._node._left_num+=num
            else:                               # else p is right child, parent._right_num+=1
                p._node._right_num+=num
            temp=p
            p=self.parent(p)

    #-----------------------------alter add method----------------------------------    

    def _add_left(self,p,e):
        temp=super()._add_left(p,e)
        self._change_num(temp,1)                # node's num  on the route which from node p to root add 1 
        
    def _add_right(self,p,e):
        temp=super()._add_right(p,e)
        self._change_num(temp,1)                # node's num  on the route which from node p to root add 1 
            
    #-----------------------------alter delete method----------------------------------    

    def _delete(self,p):
        self._change_num(p,-1)                  # node's num  on the route which from node p to root add -1
        super()._delete(p)

    #-----------------------------add method----------------------------------
    def at_index(self,i,p=None):
        '''  recursion compare the child's num with i to get the Position
            index range begin at 0
        '''
        if i >=self._size or i<0:
            raise KeyError('invalid key')
        if p==None:             
            p=self._root                                    # recur from the root node
        if i<p._left_num:                                   # if index i in p's left subtree
            return self.at_index(i,p._left)
        elif i == p._left_num:
            return self._make_position(p)                   # if index i is p
        else:
            return self.at_index(i-p._left_num-1,p._right)    # if index i in p's right subtree

    def index_of(self,p):
        '''  cycle compare the child parent with itself's position to get the index
             p is a Position class
        '''
        num=p.left_num()
        if p is self.root():                                # if p is root
            return num
        temp=self.parent(p)                                 # record the parent node
        while temp !=None
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值