Bootstrap

【优选算法】(第三十八篇)

目录

数据流中的第K⼤元素(easy)

题目解析

讲解算法原理

编写代码

前K个⾼频单词(medium)

题目解析

讲解算法原理

编写代码


数据流中的第K⼤元素(easy)

题目解析

1.题目链接:. - 力扣(LeetCode)

2.题目描述

设计⼀个找到数据流中第k⼤元素的类(class)。注意是排序后的第k⼤元素,不是第k个不同的元素。
请实现KthLargest类:KthLargest(intk,int[]nums)使⽤整数k和整数流nums初始化对象。intadd(intval)将val插⼊数据流nums后,返回当前数据流中第k⼤的元素。
⽰例:输⼊:
["KthLargest","add","add","add","add","add"]
[[3,[4,5,8,2]],[3],[5],[10],[9],[4]]
输出:
[null,4,5,5,8,8]
解释:
KthLargestkthLargest=newKthLargest(3,[4,5,8,2]);
kthLargest.add(3);//return4
kthLargest.add(5);//return5
kthLargest.add(10);//return5
kthLargest.add(9);//return8
kthLargest.add(4);//return8

提⽰:
1<=k<=10^4
0<=nums.length<=10^4
-104<=nums[i]<=10^4
-104<=val<=10^4
最多调⽤add⽅法10^4次题⽬数据保证,在查找第k⼤元素时,数组中⾄少有k个元素

讲解算法原理

解法(优先级队列):
算法思路:

我相信,看到 TopK 问题的时候,兄弟们应该能⽴⻢想到「堆」,这应该是刻在⻣⼦⾥的记忆。

编写代码

c++算法代码:

class KthLargest
{
 // 创建⼀个⼤⼩为 k 的⼩跟堆
 priority_queue<int, vector<int>, greater<int>> heap;
 int _k;
public:
 KthLargest(int k, vector<int>& nums) 
 {
 _k = k;
 for(auto x : nums)
 {
 heap.push(x);
 if(heap.size() > _k) heap.pop();
 }
 }
 
 int add(int val) 
 {
 heap.push(val);
 if(heap.size() > _k) heap.pop();
 return heap.top();
 }
};
/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

java算法代码:

class KthLargest
{
 // 创建⼀个⼤⼩为 k 的⼩根堆
 PriorityQueue<Integer> heap;
 int _k;
 public KthLargest(int k, int[] nums) 
 {
 _k = k;
 heap = new PriorityQueue<>();
 for(int x : nums)
 {
 heap.offer(x);
 if(heap.size() > _k)
 {
 heap.poll();
 }
 }
 }
 
 public int add(int val) 
 {
 heap.offer(val);
 if(heap.size() > _k)
 {
 heap.poll();
 }
 return heap.peek();
 }
}
/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest obj = new KthLargest(k, nums);
 * int param_1 = obj.add(val);
 */

前K个⾼频单词(medium)

题目解析

1.题目链接:. - 力扣(LeetCode)

2.题目描述

给定⼀个单词列表words和⼀个整数k,返回前k个出现次数最多的单词。返回的答案应该按单词出现频率由⾼到低排序。如果不同的单词有相同出现频率,按字典顺序排
序。
⽰例1:输⼊:
words=["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次。
注意:
1<=words.length<=500
1<=words[i]<=10
words[i]由⼩写英⽂字⺟组成。
k的取值范围是[1,不同words[i]的数量]
进阶:尝试以O(nlogk)时间复杂度和O(n)空间复杂度解决。

讲解算法原理

解法(堆):
算法思路:

• 稍微处理⼀下原数组:
a. 我们需要知道每⼀个单词出现的频次,因此可以先使⽤哈希表,统计出每⼀个单词出现的频
次;
b. 然后在哈希表中,选出前k⼤的单词(为什么不在原数组中选呢?因为原数组中存在重复的单
词,哈希表⾥⾯没有重复单词,并且还有每⼀个单词出现的频次)
• 如何使⽤堆,拿出前k⼤元素:
a. 先定义⼀个⾃定义排序,我们需要的是前 k ⼤,因此需要⼀个⼩根堆。但是当两个字符串的
频次相同的时候,我们需要的是字典序较⼩的,此时是⼀个⼤根堆的属性,在定义⽐较器的时候需要注意!
▪ 当两个字符串出现的频次不同的时候:需要的是基于频次⽐较的⼩根堆
▪ 当两个字符串出现的频次相同的时候:需要的是基于字典序⽐较的⼤根堆
b. 定义好⽐较器之后,依次将哈希表中的字符串插⼊到堆中,维持堆中的元素不超过 k 个;c. 遍历完整个哈希表后,堆中的剩余元素就是前 k ⼤的元素

编写代码

c++算法代码:

class Solution
{
 typedef pair<string, int> PSI;
 struct cmp
 {
 bool operator()(const PSI& a, const PSI& b)
 {
 if(a.second == b.second) // 频次相同,字典序按照⼤根堆的⽅式排列 {
 return a.first < b.first;
 }
 return a.second > b.second;
 }
 };
public:
 vector<string> topKFrequent(vector<string>& words, int k) 
 {
 // 1. 统计⼀下每⼀个单词的频次
 unordered_map<string, int> hash;
 for(auto& s : words) hash[s]++;
 // 2. 创建⼀个⼤⼩为 k 的堆
 priority_queue<PSI, vector<PSI>, cmp> heap;
 // 3. TopK 的主逻辑
 for(auto& psi : hash)
 {
 heap.push(psi);
 if(heap.size() > k) heap.pop();
 }
 // 4. 提取结果
 vector<string> ret(k);
 for(int i = k - 1; i >= 0; i--)
 {
 ret[i] = heap.top().first;
 heap.pop();
 }
 return ret;
 }
};

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();
 }
 }
 // 4. 提取结果
 List<String> ret = new ArrayList<>();
 while(!heap.isEmpty())
 {
 ret.add(heap.poll().getKey());
 }
 // 逆序数组
 Collections.reverse(ret);
 return ret;
 }
}

;