HOT100系列-堆类型题

HOT100系列-堆类型题

核心思想

例题

1、数组中的第K个最大元素

题目描述:

给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104

解题思路:

  • 快排

这里简单介绍下快速排序算法:

普通的快速排序算法首先是要在待排序数组中随机选取一个数记作 num,比 num 小的都放于 num 左边,比 num 大的都放于右边,不断迭代这个过程,直到迭代的数组中左右边界相同为止,具体步骤如下

  • 随机在数组左右边界 l, r 中选择一个数 num
  • 对 (l, r) 内的数组元素进行遍历,这个过程要使比 num 小的元素都在左边,比 num 大的元素都在右边,最后将第一个遇见的 num 置于左与右的中间位置(其实就是 num 在 l, r 的排序后的对应的最后位置,这里假设有多个 num),返回 num 位置的索引 m
  • 在(l, m)和(m+1, r)中执行上述步骤直到待排序数组片段的左边界=右边界(即待排序数组片段只有一个元素)

上述普通快排有一个非常大的问题就是当一个数组片段中有多个 num 时,它只排了其中一个,其余都未排到对应位置

而荷兰国旗的快速排序就对其做了优化,使得将所有 num 都排到了数组片段的对应位置,提高了算法效率

荷兰国旗的优化相对于普通快排最大的地方在于一次将将所有 num 都排到了数组片段的对应位置,在这个排序的过程中如下

已知arr[l... r]范围上一定有 num这个值

  • 划分数组 <x放左边,x放中间,>x放右边
  • 把全局变量first, last,更新成x区域的左右边界
  • 小于num的放first左边,大于num的放last右边
  • 若arr[i] < num 则swap (first, i), first++, i++
  • 若arr[i]>num 则swap (last, i), last--
  • 若arr[i] num 则i++

代码如下:

Java 复制代码
class Solution {

    public static int first;

    public static int last;

    public int findKthLargest(int[] nums, int k) {

        return  randomizedSelect(nums,nums.length-k);

    }

  

    //从num数组中挑选出第k大的元素

    public static int randomizedSelect(int[]nums,int k){

        int ans=0;

        for(int l=0,r=nums.length-1,num;l<=r;){

            //随机选择数字

            num=nums[l+(int)(Math.random()*(r-l+1))];

            //荷兰国旗的快排

            partition(nums,l,r,num);

            //看当前随机数字排序位置

            //有一些二分的思想

            if(first>k){

                //说明第K大的数在num左侧

                //接下来只管排左侧

                r=first-1;

            }else if(last<k){

                //说明第K大的数在num右侧

                //接下来只管排右侧

                l=last+1;

            }else{

                //第K大的数值在first和last之间

                //则由快排确定第K大元素

                ans=nums[k];

                return ans;

            }

        }

        return ans;

    }

  

    //荷兰国旗快排

    //将num数值排在数组正确位置,从小到大

    public static void partition(int[]nums,int l,int r,int num){

        first=l;

        last=r;

        int i=l;

       while(i<=last){

            if(nums[i]<num){

                //first左侧全部放比num小的元素

                swap(nums,i,first);

                first++;

                i++;

            }else if(nums[i]>num){

                swap(nums,i,last);

                last--;

            }else{

                i++;

            }

        }

  

    }

  

    public static void swap(int[] nums,int i,int j){

        int temp=nums[i];

        nums[i]=nums[j];

        nums[j]=temp;

    }

}

2、前 K 个高频元素

题目描述:

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

**输入:**nums = [1,1,1,2,2,3], k = 2

输出:[1,2]

示例 2:

**输入:**nums = [1], k = 1

输出:[1]

示例 3:

**输入:**nums = [1,2,1,2,1,2,3,1,3,2], k = 2

输出:[1,2]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

**进阶:**你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

解题思路:

  • 收集元素频次
  • 将<元素,元素频次> 放进大根堆中,以元素频次排序
  • 取大顶堆前k个即可

代码如下

java 复制代码
class Solution {

    public int[] topKFrequent(int[] nums, int k) {

        Map<Integer,Integer> map=new HashMap<>();

        int n=nums.length;

        //收集元素的频次

        for(int i=0;i<n;i++){

            map.put(nums[i],map.getOrDefault(nums[i],0)+1);

        }

        //创建大根堆

        PriorityQueue<int[]>heap=new PriorityQueue<>((a,b)->b[1]-a[1]);

        for(int key:map.keySet()){

            heap.offer(new int[]{key,map.get(key)});

        }

  

        int[]ans=new int[k];

        for(int i=0;i<k;i++){

            ans[i]=heap.poll()[0];

        }

        return ans;

    }

}

3、数据流的中位数

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

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

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。

  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入

"MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"

\[\], \[1\], \[2\], \[\], \[3\], \[\]

输出

null, null, null, 1.5, null, 2.0

解释

MedianFinder medianFinder = new MedianFinder();

medianFinder.addNum(1); // arr = [1]

medianFinder.addNum(2); // arr = [1, 2]

medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)

medianFinder.addNum(3); // arr[1, 2, 3]

medianFinder.findMedian(); // return 2.0

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNumfindMedian

解题思路

  • 1、将较大的元素放入小顶堆中,将较小的元素放入大顶堆中,这样从大顶堆底部到顶部,从小顶堆的顶部到底部就相当于对加入的数据进行了排序
  • 2、在排序的基础上,维持大小顶堆的size之差必须小于2,那么就相当于将中位数永远保持在了大小顶堆的顶部,比较大小顶堆size大小关系,即可找出中位数(具体看findMedian方法)

代码如下

Java 复制代码
class MedianFinder {

    //大根堆,存放较小的数字

    private PriorityQueue<Integer> maxheap;

    //小根堆,存放较大的数字

    private PriorityQueue<Integer> minheap;

  

    public MedianFinder() {

        maxheap=new PriorityQueue<>((a,b)->b-a);

        minheap=new PriorityQueue<>((a,b)->a-b);

    }

    public void addNum(int num) {

        if(maxheap.isEmpty() || maxheap.peek()>=num){

            maxheap.add(num);

        }else{

            minheap.add(num);

        }

        //要注意保持大小根堆的平衡,即size差不能超过2

        banlance();

    }

  

    public void banlance(){

        if(Math.abs(maxheap.size()-minheap.size())==2){

            if(maxheap.size()>minheap.size()){

                minheap.add(maxheap.poll());

            }else{

                maxheap.add(minheap.poll());

            }

        }

    }

    public double findMedian() {

        if(maxheap.size()==minheap.size()){

            return (double)(maxheap.peek()+minheap.peek())/2.0;

        }else{

            return maxheap.size()>minheap.size()?maxheap.peek():minheap.peek();

        }

    }

}
相关推荐
NAGNIP12 小时前
一文搞懂机器学习中的特征降维!
算法·面试
NAGNIP13 小时前
一文搞懂机器学习中的特征构造!
算法·面试
Learn Beyond Limits13 小时前
解构语义:从词向量到神经分类|Decoding Semantics: Word Vectors and Neural Classification
人工智能·算法·机器学习·ai·分类·数据挖掘·nlp
你怎么知道我是队长13 小时前
C语言---typedef
c语言·c++·算法
Qhumaing15 小时前
C++学习:【PTA】数据结构 7-1 实验7-1(最小生成树-Prim算法)
c++·学习·算法
Z1Jxxx16 小时前
01序列01序列
开发语言·c++·算法
汽车仪器仪表相关领域18 小时前
全自动化精准检测,赋能高效年检——NHD-6108全自动远、近光检测仪项目实战分享
大数据·人工智能·功能测试·算法·安全·自动化·压力测试
Doro再努力18 小时前
【数据结构08】队列实现及练习
数据结构·算法
清铎20 小时前
leetcode_day12_滑动窗口_《绝境求生》
python·算法·leetcode·动态规划