LeetCode基础-排序-二叉堆排序(优先队列)

本文介绍了一种数据结构——二叉堆,用于实现优先队列。文章详细阐述了二叉堆的基本概念、实现原理及其核心操作,包括上浮(swim)和下沉(sink)算法。此外还介绍了如何利用二叉堆进行排序,并通过具体实例说明了其应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有些情况下要求不一定要求数组全部有序,或者说不一定要一定性全部有序。
有些情况下先收集一些元素,找到这些元素中的最大值,然后重复这个步骤。
比如,电脑或手机上运行的多个应用程序,可以给每个程序分配一个优先队列,并总是处理下一个优先级最高的那个应用程序。

这就需要优先队列了,它支持这两种操作:

  • 删除最大元素
  • 插入元素

优先队列与队列(删除最老元素)和栈(删除最新元素)类似,但更高效。

二叉堆数据结构能够很好的实现优先队列的两种操作。二叉堆就像是一个完全二叉树。
有序的堆能够保证父结点大于两个子结点,根结点是最大结点。
这里写图片描述

位置 k 的结点的父结点的位置是 k/2,而他的子结点的位置是 2k 和 2k+1。

我们用长度为 N+1 的数据 pq[] 来表示一个大小为 N 的堆,不使用 pq[0](为了方便计算),所有堆元素放在 pq[1] 至 pq[N] 中。

首先将二叉堆排序:

  • 从下至上的有序化(上浮)swim()
  • 从上至下的有序化(下沉)sink()

如果堆的有序状态因为某个结点变得比它的父结点更大,那么就需要交换二者的顺序,以重新恢复堆的规则,直至小于其父结点。
这里写图片描述

public void swim(int k)
{
    while(k > 1 && k/2  < k)
    {
        exchange(k/2, k);
        k = k/2;
    }
}

如果堆的有序状态因为某个结点变得比它的两个子结点或是其中之一更小,那么就需要与两个子结点的较大者交换顺序,以重新恢复堆的规则,直至大于其子结点。
这里写图片描述

public void sink(int k)
{
    while(2*k < = N)
    {
        int j = 2*k;
        if(j < N && j < j+1)
        {
            j++;
        }
        if(k >= j)
        {
            break;
        }
        exchange(k, j);
        k = j;
    }
}

当插入元素时,我们将新元素加至数组末尾,改变堆的大小,然后让这个元素 swim 到合适的位置。
此操作的比较次数不超过 lgN+1

当删除最大元素时,我们从数组顶端删去最大元素,改变堆的大小,并将数组的最后一个元素放至顶端,然后让这个元素 sink 至合适的位置。
此操作的比较次数不超过 2 * lgN

这里写图片描述

任意优先队列可以变成堆排序。堆排序可以分为两个阶段:

  • 将数组构造成堆:从右至左用 sink() 函数构造子堆。
  • 下沉排序,从堆中按递减顺序取出所有元素并排序。
public void sort(int[] a)
{
    int N = a.Length;
    for(int k = N/2; k >= 1; k--)
    {
        sink(a[k]);
    }
    while(N > 1)
    {
        exchange(a[1], a[N--]);
        sink(a[1]);
    }   
}

上面的代码将 a[1] 到 a[N] 的元素进行排序,for 循环构造堆。while 循环把 a[1] 和 a[N] 交换并修复堆,直到堆变空。

这里写图片描述

常见题型:

  • 从10亿个元素中找出最大的10个。
### LeetCode排序 Python 实现 #### 解题思路 堆排序是一种基于二叉堆数据结构的选择排序方法。该算法首先构建最大堆或最小堆,接着逐步将根节点(即当前的最大值或最小值)最后一个叶子节点交换位置,并调整剩余元素重新形成堆,直到完成整个数组的排序过程。 对于Python而言,在LeetCode平台上可以借助`heapq`库来简化操作[^1]。此标准库提供了高效的函数用于创建和管理堆队列,从而使得开发者能够专注于解决问题本身而非底层细节。 #### 代码示例 下面是一个使用Python中的`heapq`模块实现堆排序的例子: ```python import heapq def heap_sort(nums): # 将列表转换成堆 heapq.heapify(nums) sorted_nums = [] while nums: # 每次弹出最小值并加入到新的有序序列中 smallest = heapq.heappop(nums) sorted_nums.append(smallest) return sorted_nums ``` 需要注意的是,上述例子实现了升序排列;如果希望得到降序的结果,则可以在输入之前先取负数处理或者直接调用`nlargest()`函数获取指定数量最大元素。 为了提高性能以及更好地适应不同类型的题目需求,还可以考虑手动建立大顶堆/小顶堆的方式来进行原地排序而不额外占用空间复杂度O(n),这通常涉及到自定义比较器的应用场景下更为适用。 #### 手动实现堆排序 这里给出不依赖于第三方库的手动实现版本,适用于更广泛的环境: ```python def build_max_heap(arr): length = len(arr) for i in range(length//2 - 1, -1, -1): max_heapify(arr, i, length) def max_heapify(arr, idx, size): largest = idx lchild = 2 * idx + 1 rchild = 2 * idx + 2 if lchild < size and arr[lchild] > arr[largest]: largest = lchild if rchild < size and arr[rchild] > arr[largest]: largest = rchild if largest != idx: arr[idx], arr[largest] = arr[largest], arr[idx] max_heapify(arr, largest, size) def heapsort_manual(arr): build_max_heap(arr) for end in range(len(arr)-1, 0, -1): arr[end], arr[0] = arr[0], arr[end] max_heapify(arr, 0, end) nums = [4, 10, 3, 5, 1] heapsort_manual(nums) print("Sorted array:", nums) ``` 这段代码展示了如何从零开始构建一个完整的堆排序流程,包括建堆(`build_max_heap`)、维护堆性质(`max_heapify`)到最后执行实际的排序逻辑(`heapsort_manual`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值