【Python数据结构】堆排序及用于优先级队列

堆排序

堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。结构如下:
在这里插入图片描述
在Python中使用层序遍历的结果将完全二叉树保存在数组中如:
在这里插入图片描述
(上面两个图来自: 博客园 dreamcatcher-cx)
使用堆排序,特征是使得最小或最大的元素总是在根部,即在数组的头部。所以也可以用作优先级队列。
实现代码如下

class HeapSort:
    def __init__(self, mode, data=None):
        """
        堆排序
        使用头节点作为哨兵,索引从1开始才是数据
        :param mode: 1 or 0
            1: 大堆模式
            0: 小堆模式
        :param data: 数组: (不是堆排序的自动转化为堆排序)
        """
        if isinstance(data, list):
            self.data = [0] + data
        else:
            self.data = [0]
         # 堆排序模式
        self.mode = mode
        # 堆内元素个数
        self.length = len(self.data) - 1
        # 初始化堆
        self.build_heap()

    def info(self):
    	"""
        显示堆内的的元素
        :return: 
        """
        print('堆顶->', self.data[1:], '<-堆尾')

    def build_heap(self):
        """
        初始化堆, 堆是一个完全二叉树属性。并用数组的层序遍历表示完全二叉树
        :return:
        """
        pos = self.length // 2
        while pos > 0:
            self.adjust_down(pos)
            pos -= 1

    def adjust_down(self, root: int):
        """
        下沉算法:
            大堆: 父节点大于孩子节点,退出。
                 父节点小于孩子节点,最大的孩子节点和父节点交换值,父节点位置变为原先孩子节点的位置,继续下沉。
            小堆: 父节点小于孩子节点,退出。
                 父节点大于孩子节点,最小的孩子节点和父节点交换值,父节点位置变为原先孩子节点的位置,继续下沉。
        :param root: 当前父节点位置
        :return:
        """
        # 将第一个的节点放入哨兵
        self.data[0] = self.data[root]
        # 找它儿子
        pos = 2 * root
        # 如果它有儿子
        while pos <= self.length:
            if self.mode:
                # 大堆算法
                # 如果有右儿子 且 右儿子大
                conditions1 = pos < self.length and self.data[pos] < self.data[pos + 1]
                # 它爸比它大
                conditions2 = self.data[0] >= self.data[pos]
            else:
                # 大堆算法
                # 如果有右儿子 且 右儿子小
                conditions1 = pos < self.length and self.data[pos] > self.data[pos + 1]
                # 它爸比它小
                conditions2 = self.data[0] <= self.data[pos]
            if conditions1:
                pos += 1
            if conditions2:
                break
            else:
                self.data[root] = self.data[pos]
                root = pos
            # 继续找它儿子
            pos *= 2
        # 找到不能再找了 最后那个节点就是它应该在的地方
        self.data[root] = self.data[0]

    def adjust_up(self, child):
        """
        上浮算法:
            大堆: 插入节点小于父节点,退出。
                 插入节点大于父节点,插入节点和父节点交换值,插入节点位置变为父节点的位置,继续上浮。
            小堆: 插入节点大于父节点,退出。
                 插入节点小于父节点,插入节点和父节点交换值,插入节点位置变为父节点的位置,继续上浮。
        :param child: 插入节点的孩子的位置
        :return:
        """
        def conditions(mode, data):
            if mode:
                # 大堆算法
                # 如果它爸不是哨兵(有爸) 而且 它大于它爸
                return pos > 0 and data[pos] < data[0]
            else:
                # 小堆算法
                # 如果它爸不是哨兵(有爸) 而且 它小于于它爸
                return pos > 0 and data[pos] > data[0]
        # 将新加入的节点放入哨兵
        self.data[0] = self.data[child]
        # 找到它爸
        pos = child // 2
        while conditions(self.mode, self.data):
            # 它爸站在它的位置上
            self.data[child] = self.data[pos]
            # 它站在它爸的位置上
            child = pos
            # 继续找它爸
            pos = child // 2
        # 找到不能再找了 最后站在的那个地方 就是该节点要呆的地方
        self.data[child] = self.data[0]

    def push(self, x: int):
        """
        堆排序插入。
        插入操作完成后,长度属性自增1,执行当前位置的上浮算法
        :param x: 整数值
        :return:
        """
        self.data.append(x)
        self.length += 1
        self.adjust_up(self.length)

    def pop(self):
        """
        堆排序弹出根元素
        弹出操作完成后,删除该位置,长度属性自减1,重新建立堆
        :return:
        """
        if self.length:
            result = self.data[1]
            del self.data[1]
            self.length -= 1
            self.build_heap()
            return result
        else:
            raise ValueError('heap have no element.')
if __name__ == '__main__':
    t = [87, 45, 78, 32, 17, 65, 53, 9]
    # mode: 0使用小堆 1使用大堆
    # data: 若传入数组,则自动转为堆排序;若不传,则是一个空的堆排序容器
    h = HeapSort(mode=0, data=t)
    h.info()
    h.pop()
    h.info()
    h.pop()
    h.info()
    h.pop()
    h.info()
    

>> 堆顶-> [9, 17, 53, 32, 87, 65, 78, 45] <-堆尾
>> 堆顶-> [17, 53, 32, 87, 65, 78, 45] <-堆尾
>> 堆顶-> [32, 53, 45, 65, 78, 87] <-堆尾
>> 堆顶-> [45, 53, 65, 78, 87] <-堆尾
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值