• 一、堆排序
    • 小顶堆
      • 举个栗子
    • 大顶堆
  • 二、前K个高频元素
    • 思路分析
  • 三、构造器代码解析

一、堆排序

要了解大顶堆和小顶堆,我们先简单了解一下堆排序。

堆排序(Heapsort)是指利用堆这种数据结构设计的一种排序算法,堆积是一个近似完全二叉树的结构,并同时满足堆积的特性:子节点的键值或者所以总是大于或大于它的父节点(注意:父节点的左节点不一定大于右节点)。

它是一种选择排序,最好、最坏、平均时间复杂度都是O(nlogn),他也是不稳定排序。

我们还要注意的一点是,我们在学习编程的时候最好把英文名字都记一下,是有好处的。

小顶堆

小顶堆就是每节点的值都小于左右节点的值

我们可以对堆中的节点按层进行编号,映射在数组中就是这样的


这个数组在逻辑上讲也是一个堆结构(小顶堆),我们用简单公式描述一个它的定义:

小顶堆:array[i] <= array[2i+1] && array[i] >=array[2i+2]

这里的i代表每个节点的键值或者索引,如果看不懂公式,将其代入上图示:

例如i = 0的时候,array[0] = 10,它的左节点为array[20+1],就是array[1] = 15,右节点array[20+2],即array[2] = 20;

此博客只有小顶堆图示,如果想了解堆排序具体步骤

举个栗子

1.我们假设现在有一个无序序列,我们将无序序列构成一个小顶堆

2.我们先从最后一个非叶子节点开始(叶子节点不用整理),一个非叶子节点为array.length/2 - 1 = 5 / 2 - 1 = 1 ,即8节点,从下至上,从左往右依次调整。

由于[8,5,3]中3最小,所以3和8交换

3.在找到第二个非叶子节点10,由于[10,3,6]中3最小,所以
和10交换

4.这时候导致[10,5,8]混乱,继续调整

这样这个无序序列就构成了小顶堆(注意:只是构建成小顶堆,并不是有序序列)。

大顶堆

大顶堆就是和小顶堆相反,每个节点的值都大于其左右子节点

将无序序列构造成大顶堆和上述小顶堆无差距,大家可以自己动手画一下

二、前K个高频元素

  1. 前 K 个高频元素(点击立即答题)
    给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:

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

提示:

1.你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。

2.你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。

3.题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。

4.你可以按任意顺序返回答案。

思路分析

这道题主要设计以下三个问题:

1.统计元素出现频率;

统计元素比较容易,我们可以直接使用HashMap来统计。

2.排序频率
由于题目要求你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小,所以我们不能直接对数组进行排序,那样复杂度就是O(nlogn)。

在这里排序频率我们可以使用一种容器配置器优先级队列

那什么是优先级队列?其实就是披着队列外衣的堆,因为优先级队列对外接口就是只从队首取元素,队尾加元素,没有其他取元素方式,看起来就像个队列。

所以这道题就是使用我们熟知的大顶堆和小顶堆,那么到底是使用大顶堆还是小顶堆呢?

很多同学看见取k个高频元素,果断大顶堆,取最大值嘛,但是你在每次更新大顶堆的时候,都把最大元素弹出去了,那谁来保留前k个高频元素?

所以,在这里我们使用小顶堆,然后遍历出现次数数组:

如果堆的元素个数小于k,直接插入堆中;

如果堆的元素个数大于k,则检查堆顶元素的频率的大小,若堆顶更大,说明至少有k个数字的频率比当前数字大,跳过当前值,否则,弹出堆顶元素,将当前元素插入堆中。

3.找出前k个高频元素

图示:

java代码:

class Solution {public int[] topKFrequent(int[] nums, int k) {//定义一个map来保存元素及其出现频率Map<Integer , Integer> map = new HashMap<Integer , Integer>();for (int num : nums) {map.put(num , map.getOrDefault(num , 0) + 1);}//int[]的第一个元素代表数数组的值,第二个元素代表这个值出现的次数//在这里自定义比较器为参数PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {public int compare(int[] m , int[] n) {return m[1] - n[1];}});//对map中的key和value进行遍历for (Map.Entry<Integer , Integer> entry : map.entrySet()) {int num = entry.getKey() , count = entry.getValue();if (queue.size() == k) {if (queue.peek()[1] < count) {queue.poll();queue.offer(new int[] {num , count});}}else {queue.offer(new int[] {num , count});}}//定义一个返回数组int[] ret = new int[k];for (int i = 0 ; i < k ; i++) {ret[i] = queue.poll()[0];}return ret;}
}

三、构造器代码解析

可能会有同学问那个比较器的作用是什么,比较器就是比较两个数据的大小,我们要比较来个元素出现频率的大小,当相减为正数,代表前者大于后者,若为负数,后者大于前者,为0则相等。

若不懂,我可以用源码解释一下,我在这里写个简单的实例(一定要看注释);

public void test1() {PriorityQueue<int[]> priorityQueue = new PriorityQueue<int[]>();PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});Integer a = 189;Integer b = 124;Integer c = 165;queue.offer(a);queue.offer(b);//进入源码 b = 124queue.offer(c);}

当执行queue.offer(b)的时候进入源码

 public boolean offer(E e) { // e 124if (e == null)throw new NullPointerException();modCount++;int i = size;if (i >= queue.length)grow(i + 1);size = i + 1;//如果i = 0直接放在堆顶if (i == 0)queue[0] = e;elsesiftUp(i, e);//否则执行这个方法,我们再次进入return true;}

当执行到siftUp时,进入源码

private void siftUp(int k, E x) { //k = 1 , e = 124 if (comparator != null)siftUpUsingComparator(k, x);//进入源码elsesiftUpComparable(k, x);}

当执行到diftUpUsingComparator(k,x)进入源码,这里就是关键代码

private void siftUpUsingComparator(int k, E x) { //k = 1 , e = 124while (k > 0) {//不带符号的向右移动一位(二进制)//上述例子中k = 1,二进制为0001,向右移动1一位0000,即parent为0//parent即父节点int parent = (k - 1) >>> 1; //parent = 0Object e = queue[parent];//进行比较(以小堆顶为例),若父节点小于子节点,则不换,否则交换//124 - 189 = -65 < 0 ,所以进行交换(一定要注意你写构造器的返回条件) if (comparator.compare(x, (E) e) >= 0)break;queue[k] = e;k = parent;}queue[k] = x;}

这就是小堆顶的执行原理再次强调,注意一定要看注释,会让你节省很多时间。

若有误,请指教!

堆排序:大顶堆和小顶堆 + 前K个高频元素相关推荐

  1. 前K个高频元素[小根堆和大根堆的使用]

    小根堆 前言 一.前K个高频元素 二.小根堆&大根堆 1.O(KlogN)大根堆 2.O(NlogK)小根堆 总结 参考文献 前言 当题目需要有序性时,果断排序,可二分快速寻找答案,或是利用有 ...

  2. (补)算法训练Day13 | LeetCode150. 逆波兰表达式求值(栈应用);LeetCode239. 滑动窗口最大值(单调队列);LeetCode347. 前K个高频元素(小顶堆,优先级队列)

    目录 LeetCode150. 逆波兰表达式求值 1. 思路 2. 代码实现 3. 复杂度分析 4. 思考 LeetCode239. 滑动窗口最大值 1. 思路 2. 代码实现 3. 复杂度分析 4. ...

  3. LeetCode——347. 前 K 个高频元素【最小堆实现】

    LeetCode--347. 前 K 个高频元素[最小堆实现] 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素.你可以按 任意顺序 返回答案. 示例1: 输入: n ...

  4. leetcode(力扣) 347. 前 K 个高频元素(优先队列 堆 哈希计数器)

    文章目录 题目描述 思路分析 法一( 哈希计数): 法二(堆): 完整代码 题目描述 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素.你可以按 任意顺序 返回答案. ...

  5. 【LeetCode笔记】347. 前K个高频元素(Java、优先队列、小顶堆、HashMap)

    文章目录 题目描述 思路 & 代码 更新版:引入 stream 流 + Lambda 题目描述 时间复杂度小于O(n*logn),否则直接sort,再遍历就很轻松. 很有学习价值的题目,第一次 ...

  6. 力扣347. 前 K 个高频元素(JavaScript,堆)

    //小顶堆class MinHeap{constructor(){this.heap=[]}//交换节点swap(i1,i2){[this.heap[i1],this.heap[i2]]=[this. ...

  7. leetcode 347. Top K Frequent Elements | 347. 前 K 个高频元素(大根堆)

    题目 https://leetcode.com/problems/top-k-frequent-elements/ 题解 参考:leetcode 215. Kth Largest Element in ...

  8. [Leetcode][第347题][JAVA][前K个高频元素][优先队列][堆][遍历set/map]

    [问题描述][中等] [解答思路] 1. 堆 复杂度 class Solution {public int[] topKFrequent(int[] nums, int k) {Map<Inte ...

  9. 堆排序及leetcode347---前 K 个高频元素

    堆排序及leetcode347-前 K 个高频元素 大顶堆:堆首元素是最大元素 小顶堆:堆首元素是最小元素 堆排序的基本思路: 将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆; 将堆顶元素 ...

最新文章

  1. 一个很好的Qt教程个人主页
  2. 拼多多谋定500个供应链品牌 农民丰收节交易会“拼农货”
  3. HTML,JS禁止鼠标右键、禁止全选、复制、粘贴的方法
  4. Hadoop-Flume-类比吸尘器图解
  5. Entity Framework Core 2.0 全局查询过滤器
  6. 终极Java日志字典:开发人员最常记录的单词是什么?
  7. Go语言获取文件的文件路径、文件名、扩展名
  8. 使用纯代码进行界面布局
  9. 剑指offer(C++)-JZ6:从尾到头打印链表(数据结构-链表)
  10. python贪吃蛇简单代码_Python贪吃蛇简单的代码
  11. 成都国税打造全能“电子税务局”
  12. html手机端图片点击放大缩小快捷键,PS放大缩小图片的快捷键是什么?PS放大缩小图片的操作技巧...
  13. IntelliJ IDEA 自定义注释作者名字
  14. 高德地图的自动定位功能
  15. jvm(17)垃圾回收器
  16. 解决Android studio 模拟器闪烁黑屏问题
  17. ISO/IEC14443
  18. 神经网络结构搜索 NAS
  19. c语言触屏滑动图片,jQuery手机触屏滑动的响应式图片轮播效果
  20. android保存到本地的图片、视频相册里找不到怎么办?

热门文章

  1. 一个登录的自动化测试用例(新手)
  2. 各省简称[转载----作为常识存储]
  3. 计算机python教程_Python 如何入门?附Python教程下载
  4. 使用servlet获得客户端与服务器的信息
  5. 如何通过看电影学英语来源
  6. [改善脑力的23条方法]
  7. 通过ZPL指令调用zebra打印机打印面单(java版)
  8. 原有人陪你颠沛流离 昨日书
  9. 开源BI工具2:apache/superset
  10. 布丁浅谈之Linux常用基本命令