LeetCode-堆排序

本文详细介绍了堆排序的算法思想,包括堆的概念、如何构造堆,特别是大顶堆的维护方法。堆排序利用优先队列实现,可以通过指定Comparator实现大顶堆或小顶堆。文中还给出了相关LeetCode题目——找出数组中出现频率前K高的元素的解题思路,通过哈希表统计频率并利用优先队列进行排序。

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

算法思想

堆的概念

:通常可以看成是一个完全二叉树的数组。(堆排序实际上是堆中元素的交换,因此可以用数组实现,并且数组的查询和修改效率比较高,能够提高性能)

堆的两种形式

  • 大顶堆:根节点大于左右孩子的节点
  • 小顶堆:根节点小于左右孩子的节点

构造堆

1、二叉树堆结构中元素与数组堆结构中元素的对应关系:(大小顶堆一样的)
在这里插入图片描述
在这里插入图片描述
那么对于某一个节点 index,它的左右孩子在数组中的下标分别为:

  • Left=index*2+1
  • Right=index*2+2

叶子结点和非叶子节点的区别:

  • 非叶子节点的下标<节点个数/2-1
  • 叶子节点的下标>节点个数/2-1

2、大顶堆的维护(核心)

自下而上地维护一个大顶堆,使其满足大顶堆的性质:非叶子节点的左右孩子都小于它的根节点

维护分为初始化时大顶堆的维护和初始化后大顶堆的维护:

  • 刚开始将待排序数组中的前k个元素复制到堆结构中的数组中,并调用HeapAdjust()方法,从非叶子节点开始,自下而上地维护
  • 而对于新插入的剩下的n-k个元素,只有当元素小于大顶堆的根节点时,才会移除根节点,并将该元素加入大顶堆。因此除了0号元素可能不满足大顶堆的性质外,它的下层的非叶子节点都满足性质,所以**维护的实质是:**维护index=0的情况。

优先队列实现堆排序

优先级队列的元素按照自然排序进行排序,或者根据构造队列时提供的 **Comparator(比较器)**进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素,依靠自然排序的优先级队列还不允许插入不可比较的对象(这样容易导致 ClassCastException)。

队列的头相当于堆的根节点

构造方法:

a.无 Comparator 参数:默认自然排序,实现小顶堆;

b.传入 Comparator:重写里面的 compare 方法,实现大顶堆。(常用)

PriorityQueue(int initialCapacity)
//使用指定的初始容量创建一个PriorityQueue,并根据自然排序对元素排序
PriorityQueue(int initialCapacity,Comparator<? super E> comparator)
//使用指定的初始容量创建一个PriorityQueue,并根据指定的比较器对元素排序

范例:(第二个构造方法)

PriorityQueue<Integer> pq=new PriorityQueue<>(k,new Comparator<Integer>(){
@Override
   public int compare(Integer o1,Integer o2){
      return o2-o1;
   }
});
/*
返回0:o1等于o2,是同一元素
返回正数:认为o2大于o1,让o2排在更靠近队列头部的位置
返回负数:认为o2小于o1,让o2排在远离队列头部的位置
这样就构成了大顶堆,相反当返回的是o1-o2时,比如返回正数,仍然认为o2大于o1,让小数o2排在了更靠近队列头部的位置
*/

相关题目

题目一

热题100-347. 前 K 个高频元素

输出数组中出现频率前K高的元素

解题思路

借助 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率

通过比较器指定排序规则,即频率越高的元素越靠近队列的头部,排序过程如下图所示:
在这里插入图片描述

代码及注解

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
    Map<Integer,Integer> requence=new HashMap<>();
    for(int num:nums){
        if(requence.containsKey(num)){
            requence.put(num,requence.get(num)+1);
        }else{
            requence.put(num,0);
        }
    }
    PriorityQueue<Integer> pq=new PriorityQueue<>(k,new Comparator<Integer>(){
        @Override
        public int compare(Integer num1,Integer num2){
            return requence.get(num1)-requence.get(num2);
        }
    });
    for(int num:requence.keySet()){
        if(pq.size()<k){//当队列中元素个数小于k时,向队列中添加元素,先将队列中元素增加到k个
            pq.offer(num);
        }else if(requence.get(num)>requence.get(pq.peek())){//当队列中头部元素大于HashMap中频率时,先将头部弹出,再向队列中添加出现频率更高的元素
            pq.poll();
            pq.offer(num);
        }
    }
    int[] res=new int[k];
    int i=0;
    while(!pq.isEmpty()){
        res[i]=pq.poll();
        i++;
    }
    return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值