优选算法--优先级队列(堆)

算法原理

优先级队列

概念

基本操作:1.返回最高优先级对象

2.添加新的对象

Priority底层使用了堆 (堆中某个节点的值总是不大于或不小于其父亲结点的值,堆是一颗完全二叉树)

堆分为小跟堆和大根堆

小根堆

大根堆

堆的存贮方式:堆是完全二叉树,可以采用顺序的方式进行高效的存储

PriorityQueue的特性:

1.导包 ;2.默认是小跟堆;3.不能插入null 4.放入的元素必须能比较大小 5.没有容量限制

6.插入和删除的时间复杂度是0(logN)

7.底层使用堆数据结构 8.线程不安全

方法:

offer()会判断是否满了,满了会扩容(1.5倍)然后会向上调整

小跟堆 return this.val-o.val

大根堆 return o.val-this.val

这里我们主要利用优先级队列来解决一类算法题

题目解析

1.最后一块石头的重量

题目描述

有一堆石头,每块石头的重量都是正整数,每一回合,从中选择最终的石头,然后把他们一起粉碎,假设石头的重量分别为x,y. 那么粉碎的结果可能如下(假设x<=y):

如果x==y,那么两块石头将会被完全粉碎;如果x!=y,那么重量为x的石头将会被完全粉碎,而重量为y的石头的新重量为y-x

最后,至多之后剩下一块石头,返回此石头的重量,如果没有石头剩下,就返回0

算法原理

解法一:暴力枚举

解法二:用堆来模拟

这里我们应该用的是大根堆

每次取出两个堆顶元素,如果两个元素相等,则不返回值继续重复操作,如果两个元素不相等,将两者的差值返回重复操作

代码实现

java 复制代码
class Solution {
    public int lastStoneWeight(int[] stones) {
        PriorityQueue<Integer> heap=new PriorityQueue<>((a,b)->b-a);
        for(int x:stones){
            heap.offer(x);
        }
        while(heap.size()>1){
            int x=heap.poll();
            int y=heap.poll();
            if(x>y){
                heap.offer(x-y);
            }
        }
        return heap.size()==0?0:heap.poll();
    }
}

2.数据流中的第 K 大元素

题目描述

设计一个找到第k大元素的类(class),注意这里要找的是排序后的第k大元素,而不是第k个不同的元素

请实现KthLargest类:

1.KthLargest(int k, int[]nums) 使用整数和整数流nums初始化对象

2.int add(int val)将val插入数据流nums后,返回当前数据流中第k大的元素

算法原理

TopK问题通常由两种解决方案:1.堆 2.快速选择

这里我们利用堆来解决

1.创建一个大小为k的堆(大根堆/小跟堆)(大小根堆的选择与题目有关)

2.循环:

1.依次进堆 2.判断堆的大小是否超过k,超过k返回堆顶元素

这里如果求的是第k大,我们利用小跟堆(这样的话大的元素在堆顶,第k大的元素就会在堆顶)

如果求的是第k小,我们要利用大根堆,这样堆顶元素是大的元素,小的元素会在堆顶

代码实现

java 复制代码
class KthLargest {
    //这里要找的是第k大元素,我们使用小跟堆,默认就是小跟堆,所以不用指定方法
    PriorityQueue<Integer> heap;
    int k;
    public KthLargest(int _k, int[] nums) {
        k=_k;
        heap=new PriorityQueue<>();
        for(int x:nums){
            add(x);
            if(heap.size()>_k){
                heap.poll();
            }
        }
    }
    
    public int add(int val) {
        heap.offer(val);
        if(heap.size()>k){
            heap.poll();
        }
        return heap.peek();
    }
}

3.前K个高频单词

题目描述

给定一个单词列表words和一个整数k,返回前k个出现次数最多的单词

返回的答案应该按单词出现频率由高到低排序,如果不同的单词有相同的出现频率,按字典顺序排序,(频次的小跟堆,频次相同的时候字典顺序的大根堆)

算法原理

topK问题

解法:利用堆来解决TopK问题

1.预处理原始的字符串数组(用哈希表先统计单词出现的次数)

2.创建大小为k的堆

3.循环: 1.让元素一次进堆 2.判断

4.提取结果 最终得到的结果需要逆序

这里堆中用Pair<String,Integer>来存,这样我们根据需要来进行堆的排序(因为这里有要求当频次相同的时候要按照字段顺序来排序)

代码实现

java 复制代码
class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        //1.先统计单词出现的次数
        Map<String,Integer> hash=new HashMap<>();
        for(String s:words){
            hash.put(s,hash.getOrDefault(s,0)+1);
        }
        //2.创建一个大小为k的堆
        PriorityQueue<Pair<String,Integer>> heap=new PriorityQueue<>(
            (a,b)->{
                if(a.getValue().equals(b.getValue())){
                    return b.getKey().compareTo(a.getKey());
                }
                return a.getValue()-b.getValue();
            }
        );
        //3.topK的主逻辑
        for(Map.Entry<String,Integer> e:hash.entrySet()){
            heap.offer(new Pair<>(e.getKey(),e.getValue()));
            if(heap.size()>k){
                heap.poll();
            }
        }
        List<String> ret=new ArrayList<>();
        while(!heap.isEmpty()){
            ret.add(heap.poll().getKey());
        }
         Collections.reverse(ret);
         return ret;

    }
}

4.数据流的中位数

题目描述

中位数是有序整数列表中的中间值,如果列表大小是偶数,则没有中间值,中位数是两个中间值的平均值

例如:arr=[2,3,4] 的中位数是3

arr=[2,3] 的中位数是(2+3)/2=2.5

算法原理

解法一:直接sort(时间复杂度很吓人)

解法二:插入排序思想(这里不做过多展开)

解法三:大小堆来维护数据流的中位数

代码实现

java 复制代码
class MedianFinder {
    PriorityQueue<Integer> left;
    PriorityQueue<Integer> right;
    public MedianFinder() {
        left=new PriorityQueue<>((a,b)-> b-a);
        right=new PriorityQueue<>((a,b)-> a-b);
    }
    
    public void addNum(int num) {
        if(left.size()==right.size()){
            if(left.isEmpty()||num<=left.peek()){
                left.offer(num);
            }else{
                right.offer(num);
                left.offer(right.poll());
            }
        }else{
            if(num<=left.peek()){
                left.offer(num);
                right.offer(left.poll());
            }else{
                right.offer(num);
            }
        }
    }
    
    public double findMedian() {
        if(left.size()==right.size()){
            return (left.peek()+right.peek())/2.0;
        }else{
            return (double)left.peek();
        }
    }
}
相关推荐
Trouvaille ~2 小时前
【优选算法篇】哈希表——空间换时间的极致艺术
c++·算法·leetcode·青少年编程·蓝桥杯·哈希算法·散列表
bbbb3652 小时前
算法调优的多目标优化与性能平衡模型的技术8
算法
Fcy6482 小时前
与二叉树有关算法题
算法·深度优先
️是782 小时前
信息奥赛一本通—编程启蒙(3346:【例60.3】 找素数)
数据结构·c++·算法
captain3762 小时前
map和set
数据结构·算法
qq_416018722 小时前
实时数据可视化库
开发语言·c++·算法
格林威2 小时前
工业相机参数解析:曝光时间与运动模糊的“生死博弈”
c++·人工智能·数码相机·opencv·算法·计算机视觉·工业相机
2401_873204652 小时前
C++中的策略模式进阶
开发语言·c++·算法
xushichao19892 小时前
C++中的职责链模式实战
开发语言·c++·算法