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

算法原理

优先级队列

概念

基本操作: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();
        }
    }
}
相关推荐
PhotonixBay9 小时前
共聚焦成像核心原理:针孔、PSF与三维形貌测量技术
人工智能·测试工具·算法
来一碗刘肉面10 小时前
C++中的引用语法
c++·算法
Black蜡笔小新10 小时前
自动化AI算法训练服务器DLTM零代码私有化赋能智慧农业用AI智能视觉守护万亩良田
人工智能·算法·自动化
Tisfy11 小时前
LeetCode 1288.删除被覆盖区间:排序(非二重循环暴力)
算法·leetcode·题解·排序
Σίσυφος190011 小时前
激光三角 光平面标定之后计算深度图
算法·平面
知无不研11 小时前
算法:Fizz Buzz解题思路
c++·算法·leetcode
阿文的代码库11 小时前
经典算法题剖析:按奇偶排序数组
数据结构·算法
AI小码11 小时前
WAIC 2026前瞻:AI产业进入拼落地的下半场
人工智能·算法·ai·程序员·大模型·编程·智能体
珠海西格电力12 小时前
零碳园区数据应用的具体场景有哪些?
大数据·人工智能·算法·架构·能源
geovindu12 小时前
CSharp: Dijkstra Algorithms
开发语言·后端·算法·c#