美文网首页
使用python实现一个大顶堆

使用python实现一个大顶堆

作者: 热血大桃子 | 来源:发表于2020-04-10 15:48 被阅读0次

    堆定义

    • 堆是一个树形结构,其底层实现是一棵完全二叉树。而完全二叉树是一层一层按照进入的顺序排成的。按照这个特性,我们可以用数组来按照完全二叉树实现堆;

    堆的一些性质

    一个大顶堆示例

    上面的示例就是一个完全二叉树,也是一个大顶堆。而大顶堆有一个性质:每一个节点的值都小于它父节点的值。(特别注意,每一个节点的值的大小与它所处的深度没有必然的联系。)

    我们可以很容易的根据任意一个节点的索引(除去根节点)找到它的父节点的索引以及其左右子节点的索引,如果当前节点的索引为index,那么:

    • 当前节点的父节点 = (index-1) / 2 (这里我们将结果取整);
    • 当前节点的左子节点 = index * 2 + 1;
    • 当前节点的右子节点 = index * 2 + 2。

    代码实现一个堆

    根据上述的堆的一些性质,我们可以自己实现一个堆。

    # 实现一个最大堆
    class MaxHeap(object):
        """
        实现一个大顶堆
        """
    
        def __init__(self):
            self.array = [] # 用一个数组存放堆中元素
    
        def heapify(self, array):
            """
            对传入的数组进行堆化
            """
            for a in array:
                self.push(a)
            return self.array
    
        def get_size(self):
            """
            返回堆的大小
            """
            return len(self.array)
    
        def _parent(self, index):
            """
            返回某个节点的父节点
            """
            if index == 0:
                raise Exception('Index 0 has no parent')
            return int((index - 1) / 2)
    
        def _left_child(self, index):
            """
            返回左孩子节点的索引
            """
            return index * 2 + 1
    
        def _right_child(self, index):
            """
            返回右边孩子节点的索引
            """
            return index * 2 + 2
    
        def _shift_up(self, k):
            """
            节点上移动,将当前节点与其父亲节点比较大小,如果比父亲节点大,
            则交换其位置,重复只执行上述过程,直到当前节点比父亲节点小。
            """
            while k > 0 and self.array[self._parent(k)] < self.array[k]:
                # 交换节点与父节点的值
                self.array[self._parent(k)], self.array[k] = self.array[k], self.array[self._parent(k)]
                k = self._parent(k)
    
        def _shift_down(self, k):
            """
            节点下移动, 当前节点与它左右孩子中最大的节点交换位置
            """
            while self._left_child(k) < self.get_size():
                choose_index = self._left_child(k)  # 左孩子的索引
                # 先比较左右孩子的大小,选择较大的那个孩子再与父亲节点进行比较
                if choose_index + 1 < self.get_size() and self.array[choose_index + 1] > self.array[choose_index]:
                    choose_index = self._right_child(k)
                if self.array[choose_index] <= self.array[k]:  # 如果最大的孩子比父亲节点小,退出循环
                    break
                # 否则父亲节点和最大的子节点交换位置
                self.array[choose_index], self.array[k] = self.array[k], self.array[choose_index]
                k = choose_index  # 进行下次循环
    
        def push(self, value):
            """
            添加一个元素后,需要对堆重新进行堆化,具体过程就是对堆尾元素执行上移操作;
            """
            self.array.append(value)
            self._shift_up(self.get_size() - 1)  # 相当于对堆进行重新堆化
    
        def pop(self):
            """
            返回堆顶元素,将堆顶元素和堆最后一个元素交换,
            然后返回最后一个元素,最后对堆顶元素进行下沉操作(重新堆化)
            """
            ret = self.find_max()
            self.array[0], self.array[self.get_size() - 1] = self.array[self.get_size() - 1], self.array[0]
            self.array.pop(-1)  # 删除最后一个元素
            self._shift_down(0)
            return ret
    
        def find_max(self):
            """
            查看堆中的最大值
            """
            if self.array:
                return self.array[0]
            else:
                raise Exception('Empty heap has no value')
    
    # 测试我们实现的大顶堆
    test = [12, 11, 10, 9, 6, 7, 8, 13]
    max_heap = MaxHeap()
    print(max_heap.heapify(test)) # 对一个数组执行堆化
    print(max_heap.pop()) # 弹出堆顶元素
    print(max_heap.array) 
    max_heap.push(14) # 推入一个元素
    print(max_heap.array)
    

    相关文章

      网友评论

          本文标题:使用python实现一个大顶堆

          本文链接:https://www.haomeiwen.com/subject/invrmhtx.html