一、问题
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例一:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例二:
输入: nums = [1], k = 1
输出: [1]
提示:
● 1 <= nums.length <= 105
● k 的取值范围是 [1, 数组中不相同的元素的个数]
● 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小
二、思路
1、统计元素出现频率 map结构:(key,value) key存放元素值,value存放出现次数,遍历整个数组求出每个元素的出现频次,时间复杂度O(logn);
2、对频率排序 按照value值进行排序;
3、找出前K个高频元素。
堆排序:nlog(n)
堆排序:维持前K个高频元素的有序集合,如K=3
三、堆相关知识
3.1 堆的相关定义
定义:堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。 故通常我们用完全二叉树来维护一个一维数组。
性质:
1)堆中某个节点的值总是不大于或不小于其父节点的值。
2)堆总是一棵完全二叉树。
完全二叉树:
满足下面两点:
1)如果将二叉树的深度设置为h,则除h层之外的所有层(1~h-1)中的节点数达到最大值(满二叉树)
2)并且h层中的所有节点都连续地集中在最左侧
就是一个完全的二叉树。
3.2 堆的分类
1)大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大。
2)小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小。
3.3 构建堆
以大根堆为例:
给定初始数组:[2, 9, 7, 8, 5, 0, 1, 6, 4, 3]
3.3.1 堆的向下调整性
(大根堆为例)
假设根节点的左右子树都是堆,但是根节点不满足堆的性质,可以通过一次向下调整来将其变成一个堆。
1)初始的树结构如下图,根节点2无法成堆,但其左右子树均能成大根堆
2)变更2的位置,先取出数值2,对比其孩子节点大小,9比7大,9移动到根节点的位置。
3)如果移动到空位上,8和5都比2大,显然不成立。因此需要移动空位下一层节点中更大的值到该空位中,8大于5,将8移动到空位中。
4)同理,2移动到现有空位上,6和4都大于2,堆无法成立。同上步骤,选择6和4中较大的值移动到空位
5)现有空位为叶子节点,已经是堆的最小节点,移动2到空位上,这时,原先的完全二叉树变为大根堆。
以上整个移动过程,就是一次堆的向下调整。
3.3.2 堆排序过程
(1)建立堆;
(2)得到堆顶元素,为最大元素;
(3)去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序;
(4)堆顶元素为第二大元素;
(5)重复步骤3,直到堆变空。
四、题解
1、统计元素出现频率 map结构:(key,value) key存放元素值,value存放出现次数,遍历整个数组求出每个元素的出现频次,时间复杂度O(logn);
2、对频率排序 按照value值进行排序;
1)如图,大根堆堆顶是列表中最大的数。取走9,得到列表中第一大的数。
2)取堆中最后一个元素,这里是3,将3移动到堆顶,得到一个除了根节点外,子树都是堆的完全二叉树
3)向下调整,得到如下图的大根堆。
如图,大根堆堆顶是列表中最大的数。取走8,得到列表中第二大的数。
4)重复2)3)过程直至取到的个数为k
3、找出前K个高频元素,输出结果