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

算法原理

优先级队列

概念

基本操作: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();
        }
    }
}
相关推荐
超级码力6665 小时前
【Latex文件架构】Latex文件架构模板
算法·数学建模·信息可视化
穿条秋裤到处跑5 小时前
每日一道leetcode(2026.04.29):二维网格图中探测环
算法·leetcode·职场和发展
Merlos_wind6 小时前
HashMap详解
算法·哈希算法·散列表
汉克老师6 小时前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
Yzzz-F9 小时前
Problem - 2205D - Codeforces
算法
智者知已应修善业9 小时前
【51单片机2个按键控制流水灯运行与暂停】2023-9-6
c++·经验分享·笔记·算法·51单片机
Halo_tjn9 小时前
Java Set集合相关知识点
java·开发语言·算法
生成论实验室10 小时前
《事件关系阴阳博弈动力学:识势应势之道》第四篇:降U动力学——认知确定度的自驱演化
人工智能·科技·神经网络·算法·架构
AI科技星10 小时前
全域数学·72分册:场计算机卷【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
科研前沿11 小时前
镜像孪生VS视频孪生核心技术产品核心优势
大数据·人工智能·算法·重构·空间计算