大、小顶堆学习

大顶堆和小顶堆

大顶堆和小顶堆是二叉堆的两种形式,它们都是完全二叉树。

自己实现大顶堆小顶堆的优势

  • 学习目的:自己动手实现大顶堆是理解其原理和运作机制的最佳方式。通过编码过程,可以深入学习堆的数据结构,包括它的插入、删除、调整等操作以及它们的时间复杂度。

  • 特定需求的定制:虽然许多编程语言的标准库中都提供了堆的实现(如 Python 的 heapq),但这些实现可能不完全符合特定场景下的需求。自己实现大顶堆可以让你根据具体需求调整数据结构的细节,例如,实现一个支持查找任意元素、删除任意元素或合并两个堆的高效版本。

  • 性能优化:在性能关键的应用中,标准库提供的堆实现可能无法满足性能需求。自己实现堆可以针对特定场景进行优化,比如使用更高效的内存结构或者减少不必要的操作来提高性能。

大顶堆(最大堆)

在大顶堆中,任何一个父节点的值都大于或等于它的子节点的值。这意味着,堆的根节点是所有节点中的最大值。

大顶堆的主要特性包括:

  • 插入操作:新元素被添加到树的末端,然后向上调整其位置,以满足大顶堆的性质。

  • 删除操作(通常指删除最大元素):根节点被删除(从堆中移除最大元素),最后一个元素被移动到根节点位置,然后向下调整其位置,以满足大顶堆的性质。

大顶堆适用于需要快速访问最大元素的场景,如实现优先队列,其中优先级最高的元素(即值最大的元素)需要被首先处理。

class MaxHeap:
    def __init__(self):
        self.heap = []  # 初始化空堆

    def parent(self, index):
        # 返回给定索引的父节点的索引
        return (index - 1) // 2

    def leftChild(self, index):
        # 返回给定索引的左子节点的索引
        return 2 * index + 1

    def rightChild(self, index):
        # 返回给定索引的右子节点的索引
        return 2 * index + 2

    def swap(self, i, j):
        # 交换堆中两个元素的位置
        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]

    def insert(self, key):
        # 向堆中插入一个新元素
        self.heap.append(key)  # 将新元素添加到堆的末尾
        self.heapifyUp(len(self.heap) - 1)  # 调整堆以维持大顶堆的性质

    def heapifyUp(self, index):
        # 从给定索引向上调整堆
        while index > 0 and self.heap[self.parent(index)] < self.heap[index]:
            # 如果当前节点比父节点大,则交换它们的位置
            self.swap(index, self.parent(index))
            index = self.parent(index)  # 更新索引到父节点,继续向上调整

    def deleteMax(self):
        # 删除并返回堆中的最大元素
        if len(self.heap) > 1:
            self.swap(0, len(self.heap) - 1)  # 将最大元素与堆的最后一个元素交换
            max_value = self.heap.pop()  # 移除并返回最后一个元素(原最大元素)
            self.heapifyDown(0)  # 从根节点开始向下调整堆
        elif self.heap:
            max_value = self.heap.pop()  # 如果堆中只有一个元素,直接移除并返回
        else:
            return None  # 如果堆为空,返回None
        return max_value

    def heapifyDown(self, index):
        # 从给定索引向下调整堆
        largest = index
        left = self.leftChild(index)
        right = self.rightChild(index)

        # 如果左子节点存在且大于当前节点,更新最大值索引
        if left < len(self.heap) and self.heap[left] > self.heap[largest]:
            largest = left

        # 如果右子节点存在且大于当前最大值,更新最大值索引
        if right < len(self.heap) and self.heap[right] > self.heap[largest]:
            largest = right

        # 如果最大值不是当前节点,交换它们,并继续向下调整
        if largest != index:
            self.swap(index, largest)
            self.heapifyDown(largest)

    def getMax(self):
        # 返回堆中的最大元素(位于根节点)
        return self.heap[0] if self.heap else None

# 使用大顶堆示例
heap = MaxHeap()
heap.insert(3)
heap.insert(2)
heap.insert(15)
heap.insert(5)
heap.insert(4)
heap.insert(45)

print("删除的最大值:", heap.deleteMax())  # 删除并打印最大值
print("现在的最大值:", heap.getMax())  # 打印当前最大值

小顶堆(最小堆)

在小顶堆中,任何一个父节点的值都小于或等于它的子节点的值。这意味着,堆的根节点是所有节点中的最小值。小顶堆的主要特性包括:

  • 插入操作:新元素被添加到树的末端,然后向上调整其位置,以满足小顶堆的性质。

  • 删除操作(通常指删除最小元素):根节点被删除(从堆中移除最小元素),最后一个元素被移动到根节点位置,然后向下调整其位置,以满足小顶堆的性质。

小顶堆适用于需要快速访问最小元素的场景,如实现优先队列,其中优先级最低的元素(即值最小的元素)需要被首先处理。

class MinHeap:
    def __init__(self):
        self.heap = []  # 初始化空堆

    def parent(self, index):
        # 返回给定索引的父节点的索引
        return (index - 1) // 2

    def leftChild(self, index):
        # 返回给定索引的左子节点的索引
        return 2 * index + 1

    def rightChild(self, index):
        # 返回给定索引的右子节点的索引
        return 2 * index + 2

    def swap(self, i, j):
        # 交换堆中两个元素的位置
        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]

    def insert(self, key):
        # 向堆中插入一个新元素
        self.heap.append(key)  # 将新元素添加到堆的末尾
        self.heapifyUp(len(self.heap) - 1)  # 调整堆以维持小顶堆的性质

    def heapifyUp(self, index):
        # 从给定索引向上调整堆
        while index > 0 and self.heap[self.parent(index)] > self.heap[index]:
            # 如果当前节点比父节点小,则交换它们的位置
            self.swap(index, self.parent(index))
            index = self.parent(index)  # 更新索引到父节点,继续向上调整

    def deleteMin(self):
        # 删除并返回堆中的最小元素
        if len(self.heap) > 1:
            self.swap(0, len(self.heap) - 1)  # 将最小元素与堆的最后一个元素交换
            min_value = self.heap.pop()  # 移除并返回最后一个元素(原最小元素)
            self.heapifyDown(0)  # 从根节点开始向下调整堆
        elif self.heap:
            min_value = self.heap.pop()  # 如果堆中只有一个元素,直接移除并返回
        else:
            return None  # 如果堆为空,返回None
        return min_value

    def heapifyDown(self, index):
        # 从给定索引向下调整堆
        smallest = index
        left = self.leftChild(index)
        right = self.rightChild(index)

        if left < len(self.heap) and self.heap[left] < self.heap[smallest]:
            smallest = left

        if right < len(self.heap) and self.heap[right] < self.heap[smallest]:
            smallest = right

        if smallest != index:
            self.swap(index, smallest)
            self.heapifyDown(smallest)  # 递归地向下调整

    def getMin(self):
        # 返回堆中的最小元素(不删除)
        return self.heap[0] if self.heap else None

# 使用小顶堆
heap = MinHeap()
heap.insert(3)
heap.insert(2)
heap.insert(15)
heap.insert(5)
heap.insert(4)
heap.insert(45)

print("删除的最小值:", heap.deleteMin())
print("现在的最小值:", heap.getMin())

实现和使用

在实际应用中大顶堆和小顶堆通常通过数组来实现

对于数组中任意位置i上的元素:

它的父节点位置是 (i-1)/2 (向下取整)。
它的左子节点位置是 2i + 1。
它的右子节点位置是 2
i + 2。
这种表示方法使得堆可以高效地进行插入和删除操作,而不需要实际的树形结构。

使用场景对比
大顶堆:适合需要频繁访问或删除集合中最大元素的场景。
小顶堆:适合需要频繁访问或删除集合中最小元素的场景。

大顶堆和小顶堆的使用场景

大顶堆

在大顶堆中,父节点的值总是大于或等于其子节点的值。这种性质使得大顶堆特别适用于需要快速访问最大元素的场景,如:

  • 优先队列:在任务调度、事件驱动编程中,需要优先处理最“重要”或优先级最高的任务。

  • Top K 问题:如从一系列元素中找出最大的 K 个元素。

  • 数据流中的最大元素:如实时监控系统中需要持续跟踪并更新最大值。

小顶堆

在小顶堆中,父节点的值总是小于或等于其子节点的值。这种性质使得小顶堆适用于需要快速访问最小元素的场景,如:

  • Dijkstra 算法:在图论中,用于寻找最短路径时,需要快速获取当前未处理节点中距离最小的节点。

  • 合并有序文件:如将多个已排序的文件合并成一个大的有序文件时,可以用小顶堆维护各个文件当前最小的未处理元素。
    中,父节点的值总是小于或等于其子节点的值。这种性质使得小顶堆适用于需要快速访问最小元素的场景,如:

  • Dijkstra 算法:在图论中,用于寻找最短路径时,需要快速获取当前未处理节点中距离最小的节点。

  • 合并有序文件:如将多个已排序的文件合并成一个大的有序文件时,可以用小顶堆维护各个文件当前最小的未处理元素。

  • 数据流中的最小元素:在需要持续跟踪最小值的应用场景中非常有用

  • 29
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值