堆排序
知识介绍请查看文章:
347. 前 K 个高频元素
本题来自 leetCode:347. 前 K 个高频元素[1]
题目详情
给定一个非空的整数数组,返回其中出现频率前
k
高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
说明:
你可以假设给定的
k
总是合理的,且
1 ≤ k ≤ 数组中不相同的元素的个数
。
你的算法的时间复杂度必须优于
O(n*logn)
,
n
是数组的大小。
题目分析
具体实现
class Solution {
public ListInteger topKFrequent(int[] nums, int k) {
// 统计各数出现的频率
SetMap.EntryInteger, Integer entrySet = count(nums);
Map.EntryInteger, Integer[] heap = new Map.Entry[k];
IteratorMap.EntryInteger, Integer iterator = entrySet.iterator();
for (int i = 0; i k; i++) {
heap[i] = iterator.next();
iterator.remove();
}
// 排序
heapSort(heap, entrySet, k);
// 输出
return output(heap);
}
public SetMap.EntryInteger, Integer count(int[] nums) {
MapInteger, Integer table = new HashMap();
for (int num : nums) {
Integer count = table.getOrDefault(num, 0);
table.put(num, count + 1);
}
return table.entrySet();
}
public ListInteger output(Map.Entry[] heap) {
ListInteger topK = new ArrayList();
for (int i = 0; i heap.length; i ++) {
topK.add((Integer)heap[i].getKey());
}
return topK;
}
/**
* 大根堆排序后的顺序为从小到大,只需要前k个元素有序
* @param heap
* @param entrySet
*/
public void heapSort(Map.Entry[] heap, SetMap.EntryInteger, Integer entrySet, int k) {
// 小根堆
BiPredicateMap.Entry, Map.Entry predicate = (a, b) - (Integer) a.getValue() (Integer) b.getValue();
// 先初始化最小堆
buildHeap(heap, k, predicate);
// 获取前k个频率高的值
for(Map.Entry entry : entrySet) {
// 小根堆堆顶的元素是最小值,若新加入的值大于堆顶堆顶元素,则置换
if(predicate.test(heap[0], entry)) {
heap[0] = entry;
// 由于堆结构发生了变化,需要继续对根元素nums[0]进行调整
adjustHeap(heap, 0, k, predicate);
}
}
}
/**
* 先初始化小根堆,使其满足所有非叶子节点:父节点的值小于子节点的值;
* @param heap
* @param length
*/
public void buildHeap(Map.Entry[] heap, int length, BiPredicateMap.Entry, Map.Entry predicate) {
//最后一个非叶子节点的位置为`n / 2 - 1`,从后向前,依次堆化
for(int i = (length 1) - 1; i = 0; i --) {
adjustHeap(heap, i, length, predicate);
}
}
/**
* 堆化,使得父节点的值小于子节点的值
* @param heap 堆数组
* @param parent 待调整的非叶子节点
* @param length 调整范围
*/
public void adjustHeap(Map.Entry[] heap, int parent, int length, BiPredicateMap.Entry, Map.Entry predicate) {
// index位置节点的左子节点的位置为2 * i + 1
int left = (parent 1) + 1;
// index位置节点的右子节点的位置为2 * i + 2
int right = (parent 1) + 2;
// 存储父节点、左子节点、右子节点三者最小值的位置
int temp = parent;
// 比较父节点和左子节点的大小,
if(left length && predicate.test(heap[left], heap[temp])) {
temp = left;
}
// 比较父节点和右子节点的大小
if(right length && predicate.test(heap[right], heap[temp])) {
temp = right;
}
// 若父节点的值不是三者最小的
if(temp != parent) {
// 则交换和最小值子节点的值
swap(heap, parent, temp);
// 并继续调整子节点,使其堆化
adjustHeap(heap, temp, length, predicate);
}
}
/**
* 交换数组heap中i和j的值
* @param heap
* @param i
* @param j
*/
public void swap(Map.Entry[] heap, int i, int j) {
Map.Entry temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
}
复杂度分析
时间复杂度:
O(n*log(k))
。其中:
空间复杂度:
O(n)
,存储哈希表的开销。
692. 前 K 个高频单词
本题来自 LeetCode:692. 前 K 个高频单词[2]
题目详情
给一非空的单词列表,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。
示例 1:
输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 "i" 在 "love" 之前。
示例 2:
输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,
出现次数依次为 4, 3, 2 和 1 次。
注意:
扩展练习:尝试以
O(nlogk)
时间复杂度和
O(n)
空间复杂度解决。
题目分析
题目实现
class Solution {
public ListString topKFrequent(String[] words, int k) {
// 统计各单词出现的频率
SetMap.EntryString, Integer entrySet = count(words);
Map.EntryString, Integer[] heap = new Map.Entry[k];
IteratorMap.EntryString, Integer iterator = entrySet.iterator();
for (int i = 0; i k; i++) {
heap[i] = iterator.next();
iterator.remove();
}
// 排序
heapSort(heap, entrySet, k);
// 输出
return output(heap);
}
public SetMap.EntryString, Integer count(String[] nums) {
MapString, Integer table = new HashMap();
for (String num : nums) {
Integer count = table.getOrDefault(num, 0);
table.put(num, count + 1);
}
return table.entrySet();
}
public ListString output(Map.Entry[] heap) {
ListString topK = new ArrayList();
for (int i = 0; i heap.length; i ++) {
topK.add((String)heap[i].getKey());
}
return topK;
}
/**
* 大根堆排序后的顺序为从小到大,只需要前k个元素有序
* @param heap
* @param entrySet
*/
public void heapSort(Map.Entry[] heap, SetMap.EntryString, Integer entrySet, int k) {
// 小根堆,同时根据首字母排序
BiPredicateMap.Entry, Map.Entry predicate = (a, b) - {
if(a.getValue().equals(b.getValue())) {
String ka = (String) a.getKey();
String kb = (String) b.getKey();
return ka.compareTo(kb) 0;
}
return (Integer) a.getValue() (Integer) b.getValue();
};
// 先初始化最小堆
buildHeap(heap, k, predicate);
// 获取前k个频率高的值
for(Map.Entry entry : entrySet) {
// 小根堆堆顶的元素是最小值,若新加入的值大于堆顶堆顶元素,则置换
if(predicate.test(heap[0], entry)) {
heap[0] = entry;
// 由于堆结构发生了变化,需要继续对根元素nums[0]进行调整
adjustHeap(heap, 0, k, predicate);
}
}
// 调整堆,进入排序逻辑
for (int i = k - 1; i 0 ; i --) {
swap(heap, 0, i);
adjustHeap(heap, 0, i, predicate);
}
}
/**
* 先初始化小根堆,使其满足所有非叶子节点:父节点的值小于子节点的值;
* @param heap
* @param length
*/
public void buildHeap(Map.Entry[] heap, int length, BiPredicateMap.Entry, Map.Entry predicate) {
//最后一个非叶子节点的位置为`n / 2 - 1`,从后向前,依次堆化
for(int i = (length 1) - 1; i = 0; i --) {
adjustHeap(heap, i, length, predicate);
}
}
/**
* 堆化,使得父节点的值小于子节点的值
* @param heap 堆数组
* @param parent 待调整的非叶子节点
* @param length 调整范围
*/
public void adjustHeap(Map.Entry[] heap, int parent, int length, BiPredicateMap.Entry, Map.Entry predicate) {
// index位置节点的左子节点的位置为2 * i + 1
int left = (parent 1) + 1;
// index位置节点的右子节点的位置为2 * i + 2
int right = (parent 1) + 2;
// 存储父节点、左子节点、右子节点三者最小值的位置
int temp = parent;
// 比较父节点和左子节点的大小,
if(left length && predicate.test(heap[left], heap[temp])) {
temp = left;
}
// 比较父节点和右子节点的大小
if(right length && predicate.test(heap[right], heap[temp])) {
temp = right;
}
// 若父节点的值不是三者最小的
if(temp != parent) {
// 则交换和最小值子节点的值
swap(heap, parent, temp);
// 并继续调整子节点,使其堆化
adjustHeap(heap, temp, length, predicate);
}
}
/**
* 交换数组heap中i和j的值
* @param heap
* @param i
* @param j
*/
public void swap(Map.Entry[] heap, int i, int j) {
Map.Entry temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
}
复杂度分析
时间复杂度:
O(n*log(k))
。其中:
空间复杂度:
O(n)
,存储哈希表的开销。
参考资料
前 K 个高频元素: https://leetcode-cn.com/problems/top-k-frequent-elements/
前K个高频单词: https://leetcode-cn.com/problems/top-k-frequent-words/
原文始发于微信公众号(xiaogan的技术博客):