目录
数据流中的第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;
}
}