在python中大小平衡二叉树堆

时间:2012-02-11 23:57:31

标签: python heap binary-tree

所以我正在做一些功课,我必须完成一个大小平衡的二叉树堆实现,而我的入队函数遇到了一些麻烦。我们得到了一些代码开始,我对如何访问类等有点困惑。在下面的代码中,我写的唯一的东西是空函数(如果它没有传递给树,它不起作用,但我现在不太担心它。)和enqueue函数确实添加了前三个元素(我不确定它是否正确添加)然后失败。我认为我在enqueue函数中遇到的问题是我不确切知道如何将TreeNode添加到堆中,以及如何正确访问“heap.lchild.size”之类的内容,如果这样甚至可能的话。当我们完成时,被注释掉的结尾应该被用来测试我们的程序,但是我把它评论出来并复制了一段只测试入队函数的文件。

import random

class TreeNode( object ):
    """
       A (non-empty) binary tree node for a size-balanced binary tree heap.
       SLOTS:
         key: Orderable
         value: Any
         size: NatNum; the size of the sub-tree rooted at this node
         parent: NoneType|TreeNode; the parent node
         lchild: NoneType|TreeNode; the node of the left sub-tree
         rchild: NoneType|TreeNode; the node of the right sub-tree
    """
    __slots__ = ( 'key', 'value', 'size', 'parent', 'lchild', 'rchild' )

    def __init__( self, key, value, parent ):
        self.key = key
        self.value = value
        self.size = 1
        self.parent = parent
        self.lchild = None
        self.rchild = None

    def __str__( self ):
        slchild = str(self.lchild)
        srchild = str(self.rchild)
        skv = str((self.key, self.value)) + " <" + str(self.size) + ">"
        pad = " " * (len(skv) + 1)
        s = ""
        for l in str(self.lchild).split('\n'):
            s += pad + l + "\n"
        s += skv + "\n"
        for l in str(self.rchild).split('\n'):
            s += pad + l + "\n"
        return s[:-1]

class SBBTreeHeap( object ):
    """
       A size-balanced binary tree heap.
       SLOTS:
         root: NoneType|TreeNode
    """
    __slots__ = ( 'root' )

    def __init__( self ):
        self.root = None

    def __str__( self ):
        return str(self.root)

def checkNode( node, parent ):
    """
       checkNode: NoneType|TreeNode NoneType|TreeNode -> Tuple(NatNum, Boolean, Boolean, Boolean, Boolean)
       Checks that the node correctly records size information,
       correctly records parent information, satisfies the
       size-balanced property, and satisfies the heap property.
    """
    if node == None:
        return 0, True, True, True, True
    else:
        lsize, lsizeOk, lparentOk, lbalanceProp, lheapProp = checkNode( node.lchild, node )
        rsize, rsizeOk, rparentOk, rbalanceProp, rheapProp = checkNode( node.rchild, node )
        nsize = lsize + 1 + rsize
        nsizeOk = node.size == nsize
        sizeOk = lsizeOk and rsizeOk and nsizeOk
        nparentOk = node.parent == parent
        parentOk = lparentOk and rparentOk and nparentOk
        nbalanceProp = abs(lsize - rsize) <= 1
        balanceProp = lbalanceProp and rbalanceProp and nbalanceProp
        nheapProp = True
        if (node.lchild != None) and (node.lchild.key < node.key):
            nheapProp = False
        if (node.rchild != None) and (node.rchild.key < node.key):
            nheapProp = False
        heapProp = lheapProp and rheapProp and nheapProp
        return nsize, sizeOk, parentOk, balanceProp, heapProp

def checkHeap( heap ):
    """
       checkHeap: SBBTreeHeap -> NoneType
       Checks that the heap is a size-balanced binary tree heap.
    """
    __, sizeOk, parentOk, balanceProp, heapProp = checkNode( heap.root, None )
    if not sizeOk:
        print("** ERROR **  Heap nodes do not correctly record size information.")
    if not parentOk:
        print("** ERROR **  Heap nodes do not correctly record parent information.")
    if not balanceProp:
        print("** Error **  Heap does not satisfy size-balanced property.")
    if not heapProp:
        print("** Error **  Heap does not satisfy heap property.")
    assert(sizeOk and parentOk and balanceProp and heapProp)
    return


def empty(heap):
    """
       empty: SBBTreeHeap -> Boolean
       Returns True if the heap is empty and False if the heap is non-empty.
       Raises TypeError if heap is not an instance of SBBTreeHeap.
       Must be an O(1) operation.
    """

    if not SBBTreeHeap:
        print("** Error **  Heap is not an instance of SBBTreeHeap.")
    if heap.root == None:
        return True
    else:
        return False

def enqueue( heap, key, value ):
    """
       enqueue: SBBTreeHeap Orderable Any -> NoneType
       Adds the key/value pair to the heap.
       Raises TypeError if heap is not an instance of SBBTreeHeap.
       Must be an O(log n) operation.
    """
#    print('heap has entered enqueue')
#    print(str(heap))
    if empty(heap):
        heap.root = TreeNode(key, value, None)
    if heap.root.size < 3:
        if heap.root.lchild != None:
            if heap.root.rchild == None:
                heap.root.rchild = TreeNode(key, value, heap.root)
                heap.root.size += 1
        elif heap.root.lchild == None:
            heap.root.lchild = TreeNode(key, value, heap.root)
            heap.root.size += 1
    else:
        if heap.lchild.size >= heap.rchild.size:
            heap.lchild = TreeNode(key, value, heap.root)
        else:
            heap.rchild = TreeNode(key, value, heap.root)





def frontMin( heap ):
    """
       frontMin: SBBTreeHeap -> Tuple(Orderable, Any)
       Returns (and does not remove) the minimum key/value in the heap.
       Raises TypeError if heap is not an instance of SBBTreeHeap.
       Raises IndexError if heap is empty.
       Precondition: not empty(heap)
       Must be an O(1) operation.
    """
    ## COMPLETE frontMin FUNCTION ##


def dequeueMin( heap ):
    """
       dequeueMin: SBBTreeHeap -> NoneType
       Removes (and does not return) the minimum key/value in the heap.
       Raises TypeError if heap is not an instance of SBBTreeHeap.
       Raises IndexError if heap is empty.
       Precondition: not empty(heap)
       Must be an O(log n) operation.
    """
    ## COMPLETE dequeueMin FUNCTION ##


def heapsort( l ):
    """
       heapsort: ListOfOrderable -> ListOfOrderable
       Returns a list that has the same elements as l, but in ascending order.
       The implementation must a size-balanced binary tree heap to sort the elements.
       Must be an O(n log n) operation.
    """
    ## COMPLETE heapsort FUNCTION ##


######################################################################
######################################################################

if __name__ == "__main__":
#    R = random.Random()
#    R.seed(0)
#    print(">>> h = SBBTreeHeap()");
#    h = SBBTreeHeap()
#    print(h)
#    checkHeap(h)
#    for v in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
#        k = R.randint(0,99)
#        print(">>> enqueue(h," + str(k) + "," + str(v) + ")")
#        enqueue(h, k, v)
#        print(h)
#        checkHeap(h)
#    while not empty(h):
#        print(">>> k, v = frontMin(h)")
#        k, v = frontMin(h)
#        print((k, v))
#        print(">>> dequeueMin(h)")
#        dequeueMin(h)
#        print(h)
#        checkHeap(h)
#    for i in range(4):
#        l = []
#        for x in range(2 ** (i + 2)):
#            l.append(R.randint(0,99))
#        print(" l = " + str(l))
#        sl = heapsort(l)
#        print("sl = " + str(sl))
#
#heap = SBBTreeHeap()
#print(empty(heap))

    R = random.Random()
    R.seed(0)
    print(">>> h = SBBTreeHeap()");
    h = SBBTreeHeap()
    print(h)
    checkHeap(h)
    for v in 'ABCDEFG':
        k = R.randint(0,99)
        print(">>> enqueue(h," + str(k) + "," + str(v) + ")")
        enqueue(h, k, v)
        print(h)
        checkHeap(h)

2 个答案:

答案 0 :(得分:0)

不要担心if size < 3的东西。

我建议你先写出frontMin,这应该很容易。

然后你可以写heapsort。它应该主要调用其他方法,而不是自己做任何努力。

添加/ enqueue时,您需要担心以下四种情况:

  • 左右都是无
  • 右是无
  • 左边是无
  • 既不是无

其中一些案例比其他案件简单得多。

dequeueMin会有同样的考虑因素。

我建议你仔细阅读the wiki page

我建议先建立一个二进制堆,然后确保它的大小平衡。

其中一些案件很容易,有些则不然。

答案 1 :(得分:0)

你所拥有的是一个良好的开端,因为它确实适用于前3个元素。要使其适用于所有情况,您需要知道每个新TreeNode应该去哪里。在普通二进制堆的情况下,我们知道新节点将进入底层最左边的空闲点。但是对于大小平衡的二叉树,它需要满足“每个节点的两个子树的大小从不相差超过1”的约束(实验室讲义的第1页)。要确保这一点成立,您需要替换向root的左子项和右子项添加新节点。

  • 如果TreeNode的左侧孩子的大小为k,而右侧孩子的大小为k-1,则您希望添加到正确的子树(如果不是,则为大小;左右子树的数量将因2)而异。

  • 如果TreeNode的左右子项都具有相同的大小,那么只要您保持一致,添加新节点的子树就没关系。

当您查看的当前节点的大小大于2时(意味着它同时具有左子节点和右子节点),这两点将涵盖。另外两种情况是节点的大小为2(有1个子节点)或1(有0个子节点)。如果大小为2,只需将新节点添加到任何一方为空,如果大小为1,则可以选择添加新节点的哪一侧,但要保持一致。找到添加新节点的位置后,在其键小于其父键的情况下进行筛选。

出队有点复杂。您需要找到刚刚添加的堆中的位置,将其与根交换,删除该位置(现在具有root的键/值),并在其根密钥大于其子密钥时筛选根节点(两个孩子的最小孩子的钥匙)。

对于enqueue和dequeue,请记住在堆中或堆上时更新受影响节点的大小。

希望有所帮助。