用python实现堆排序、优先级队列

堆排序是一个优秀的算法,但在实际应用中,性能比快速排序差一些。尽管如此,堆这一数据结构仍然有很多应用。接下来我们要介绍堆的一个常见的应用:优先级队列。

一、堆排序

堆排序图解

数组 A [ 0 , . . . , A . l e n g t h − 1 ] A[0,...,A.length-1] A[0,...,A.length1],给定一个节点的下标i,我们很容易计算得到它的父节点、左孩子和右孩子的下标。

def left(i): return 2 * i + 1
def right(i): return 2 * i + 2
def parent(i): return (i-1)//2
以最大堆为例,首先来看最大堆的性质:

A [ p a r e n t ( i ) ] > = A [ i ] A[parent(i)]>=A[i] A[parent(i)]>=A[i]
意思就是任意节点的父节点的值必须大于等于该节点自己的值。

维护最大堆的性质:
def max_heapify(A, i, heap_size):
    """
    维护最大堆性质
    :param i: 下标
    :return:
    """

    l = left(i)
    r = right(i)

    if l < heap_size and A[l] > A[i]:
        largest = l
    else:
        largest = i

    if r < heap_size and A[r] > A[largest]:
        largest = r

    if largest != i:
        exchange(i, largest)
        max_heapify(A, largest)

上面函数的输入为一个数组 A A A和它的一个下标 i i i,以及堆的大小 h e a p s i z e heap_size heapsize。在调用 m a x _ h e a p i f y max\_heapify max_heapify时,我们假定节点为 l e f t ( i ) left(i) left(i) r i g h t ( i ) right(i) right(i)的二叉树都是最大堆,但这时 A [ i ] A[i] A[i]有可能小于其孩子,这样就违背了最大堆的性质。 m a x _ h e a p i f y max\_heapify max_heapify通过让 A [ i ] A[i] A[i]的值在最大堆中“逐级下降”,从而使得下标 i i i为根节点的子树重新遵循最大堆的性质。

建堆:
def build_max_heap(A):
	heap_size = len(A)
    for i in range(heap_size //2, -1, -1):
        max_heapify(A,i,heap_size)

用自底向上的方法,对每个非叶节点(i <= heap_size//2)利用过程 m a x _ h e a p i f y max\_heapify max_heapify把一个数组转换为最大堆(本质上还是数组,只不过值的位置发生了变化)。建堆的时间复杂度是 O ( n ) O(n) O(n)

堆排序算法:
 def heap_sort(A):
     build_max_heap(A)
     heap_size = len(A)
     for i in range(len(A)-1, 0, -1):
         exchange(0, i)
         heap_size -= 1
         max_heapify(A,0,heap_size)

初始时候,建立一个最大堆。然后将第一个元素和最后一个元素交换后,去掉最后一个元素( h e a p _ s i z e − = 1 heap\_size-=1 heap_size=1),并使用 m a x _ h e a p i f y ( A , 0 , h e a p _ s i z e ) max\_heapify(A,0,heap\_size) max_heapify(A,0,heap_size)保持最大堆性质。不断重复这个过程,直到堆的大小降为1。

至此堆排序算法讲完了。建立一个Heap类,整合上述方法:
class Heap:

    def __init__(self, values):
        if type(values) is not list:
            raise ValueError("{} initial values must be list".format(self.__class__.__name__))
        self.values = values
        self.heap_size = len(values)

    @staticmethod
    def left(i): return 2 * i + 1

    @staticmethod
    def right(i): return 2 * i + 2

    @staticmethod
    def parent(i): return (i-1)//2

    def exchange(self, a, b):
        tmp = self.values[a]
        self.values[a] = self.values[b]
        self.values[b] = tmp

    def max_heapify(self, i):
        """
        维护最大堆性质
        :param i: 下标
        :return:
        """

        l = self.left(i)
        r = self.right(i)

        if l < self.heap_size and self.values[l] > self.values[i]:
            largest = l
        else:
            largest = i

        if r < self.heap_size and self.values[r] > self.values[largest]:
            largest = r

        if largest != i:
            self.exchange(i, largest)
            self.max_heapify(largest)

    def build_max_heap(self):
        for i in range(self.heap_size//2, -1, -1):
            self.max_heapify(i)

    def heap_sort(self):
        self.build_max_heap()
        for i in range(self.heap_size-1, 0, -1):
            self.exchange(0, i)
            self.heap_size -= 1
            self.max_heapify(0)

二、优先级队列

一个最大优先队列Q支持以下操作:

i n s e r t ( x ) insert(x) insert(x):把元素 x x x插入 Q Q Q队列。
m a x i m u m ( ) maximum() maximum():返回 Q Q Q队列中最大元素。
p o p ( ) pop() pop():去掉并返回 Q Q Q队列中最大元素。
i n c r e a s e _ k e y ( i , x ) increase\_key(i,x) increase_key(i,x):将下标 i i i的值增大到 x x x

优先级队列本质也是一个堆,在堆的基础上增加上述方法。
class PriorityQueue(Heap):

    def __init__(self, values):
        super(PriorityQueue, self).__init__(values)
        self.build_max_heap()

    def add(self, value):
        self.values.append(float("-inf"))
        self.heap_size += 1
        self.increase_value(self.heap_size-1, value)

    def pop(self):
        if self.heap_size < 1:
            raise Exception("heap underflow")
        self.exchange(0, self.heap_size-1)
        value = self.values.pop()
        self.heap_size -= 1
        self.max_heapify(0)
        return value

    def maximum(self):
        return self.values[0]

    def increase_value(self, i, value):
        """
        增加第i个元素的值
        :param i: 下标
        :param value:值增加为value
        :return:
        """
        if value < self.values[i]:
            raise Exception("new value is smaller than current value")
        self.values[i] = value
        while i > 0 and self.values[i] > self.values[self.parent(i)]:
            self.exchange(i, self.parent(i))
            i = self.parent(i)

过程 p o p pop pop与过程 h e a p _ s o r t heap\_sort heap_sort f o r for for循环体的部分很相似,弹出第一个元素, h e a p _ s i z e − = 1 heap\_size -=1 heap_size=1,维护堆性质。

值得一提的是过程 i n c r e a s e _ k e y increase\_key increase_key,当前元素增大后,不断与其父节点进行比较,如果比父元素值大,则交换它们,直到当前元素小于父元素或没有父元素为止。

而过程 i n s e r t insert insert就是在堆底加入了一个负无穷的元素,然后将调用过程 i n c r e a s e _ k e y increase\_key increase_key将它增加为输入值的大小。

测试
heap = PriorityQueue([6, 10, 8, 2, 4])
print("intial", ":", heap.values)
heap.add(16)
print("add", 16, ":", heap.values)
heap.add(11)
print("add", 11, ":", heap.values)
heap.pop()
print("pop", ":", heap.values)
print("maximum", ":", heap.maximum())

输出
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值