PriorityQueue优先级队列——java中的堆

堆的应用——前K个高频元素

此文依托力扣题目“前K个高频元素”主要讲解了:getOrDefault( ) 和 PriorityQueue 的用法

上题:
在这里插入图片描述
题目分析:
此题题意很简单,主要为:
1.统计元素出现的频率
2.对频率排序
3.找出前K个高频元素

统计元素出现的频率,这一类问题可以使用map,
key:数组中的元素;
value:元素出现的频率;
对频率进行排序,这里我们使用PriorityQueue(优先级队列)是最简便的。
那么什么是优先级队列呢?
PriorityQueue 就是一个披着队列外衣的堆。

先上代码:

public int[] topKFrequent(int[] nums, int k) {
        int[] result = new int[k];
        //key:数组中的数字 ,value:出现的频率(权重)
        Map<Integer,Integer> map = new HashMap<>();
        //计算所有数出现的频率
        for(int i : nums){
        	//统计元素i出现的频率
            map.put(i,map.getOrDefault(i,0)+1);
        }
        //建立一个小根堆,PriorityQueue 优先级队列 实现堆
        PriorityQueue<Map.Entry<Integer,Integer>> queue;
        queue = new PriorityQueue<>((o1,o2) -> o1.getValue() - o2.getValue());
        Set<Map.Entry<Integer,Integer>> entries = map.entrySet();
        for(Map.Entry<Integer,Integer> entry : entries){
            queue.offer(entry);
            if(queue.size() > k){
                queue.poll();
            }
        }
        for(int i = k-1;i >= 0;i--){
            result[i] = queue.poll().getKey();
        }
        return result;
    }

代码中知识盲点分析:

1.map.getOrDefault(Object key, V defaultValue)
如果在Map中存在key,则返回key所对应的的value。
如果在Map中不存在key,则返回defaultValue。
此方法常用来统计数字出现的次数
源码如下:

default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }

2.PriorityQueue

PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。

1.优先队列不允许空值,而且不支持non-comparable(不可比较)的对象,比如用户自定义的类。优先队列要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
2.优先队列的头是基于自然排序或者Comparator排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
3.优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加。
4.PriorityQueue是非线程安全的,所以Java提供了PriorityBlockingQueue(实现BlockingQueue接口)用于Java多线程环境。

代码中的Map.Entry<Integer,Integer>对象没有提供任何类型的排序,所以
当我们用它建立优先队列时,应该提供一个用于排序的比较器对象。

PriorityQueue<Map.Entry<Integer,Integer>> queue;
比较器对象的几种写法:

写法一:

PriorityQueue<Map.Entry<Integer,Integer>> queue = new PriorityQueue<>(
            new Comparator<Map.Entry<Integer, Integer>>() {
                @Override
                public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                	//升序! 如果o1.getValue() - o2.getValue() < 0
                	//不交换位置,o2直接插在o1后
                    return o1.getValue() - o2.getValue();
             
                    //降序:o2.getValue() - o1.getValue()
                }
            }
    );

写法二:

PriorityQueue<Map.Entry<Integer,Integer>> queue;
        queue = new PriorityQueue<>((o1,o2) -> o1.getValue() - o2.getValue());

写法三:

//调用comparingInt方法,当然也有comparingString()
PriorityQueue<Map.Entry<Integer, Integer>> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(Map.Entry::getValue));

写在最后:
如果对建堆代码感兴趣,请看源码,这里贴一部分
向上调整:

private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值