215. 数组中的第K个最大元素
给定整数数组
nums
和整数k
,请返回数组中第**k**
个最大的元素。请注意,你需要找的是数组排序后的第
k
个最大的元素,而不是第k
个不同的元素。你必须设计并实现时间复杂度为
O(n)
的算法解决此问题。示例 1:
输入: [3,2,1,5,6,4], k = 2 输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4 输出: 4
提示:
1 <= k <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
借助快速排序的思想解决topK问题,快速选择将数组划分为三块,对应三种情况进行查找,不断递归即可:
[left, l - 1] [l, r] [r + 1, right]
- 当k小于等于右区间的长度,既
k <= right - r
时,继续在右区间查找第k大的数。 - 当k小于等于右区间+中间相同字符的长度,既
k <= right - l + 1
时,第k大的数就在中间区间为key
。 - 当k大于右区间+中间相同字符的长度时,既k在左区间时,在左区间查找第k大的数减去右区间+中间相同字符的长度,既第
k - (right - l + 1)
大的数。
cpp
int findKthLargest(vector<int>& nums, int k) {
srand(time(nullptr));
int left = 0, right = nums.size() - 1;
return topK(nums, left, right, k);
}
int topK(vector<int>& nums, int left, int right, int k) {
if (left == right)
return nums[left];
// 随机获取key
int key = nums[rand() % (right - left + 1) + left];
// 数组划分为三块
int i = left, l = left, r = right;
while (i <= r) {
if (nums[i] < key)
swap(nums[l++], nums[i++]);
else if (nums[i] > key)
swap(nums[r--], nums[i]);
else
i++;
}
// [left, l - 1] [l, r] [r + 1, right]
if (k <= right - r)
return topK(nums, r + 1, right, k);
else if (k <= right - l + 1)
return key;
else
return topK(nums, left, l - 1, k - (right - l + 1));
}
快速排序 的思想解决topK问题,快速选择将数组划分为三块,对应三种情况进行查找,不断递归即可:
[left, l - 1] [l, r] [r + 1, right]
- 当k小于等于右区间的长度,既
k <= right - r
时,继续在右区间查找第k大的数。 - 当k小于等于右区间+中间相同字符的长度,既
k <= right - l + 1
时,第k大的数就在中间区间为key
。 - 当k大于右区间+中间相同字符的长度时,既k在左区间时,在左区间查找第k大的数减去右区间+中间相同字符的长度,既第
k - (right - l + 1)
大的数。
cpp
int findKthLargest(vector<int>& nums, int k) {
srand(time(nullptr));
int left = 0, right = nums.size() - 1;
return topK(nums, left, right, k);
}
int topK(vector<int>& nums, int left, int right, int k) {
if (left == right)
return nums[left];
// 随机获取key
int key = nums[rand() % (right - left + 1) + left];
// 数组划分为三块
int i = left, l = left, r = right;
while (i <= r) {
if (nums[i] < key)
swap(nums[l++], nums[i++]);
else if (nums[i] > key)
swap(nums[r--], nums[i]);
else
i++;
}
// [left, l - 1] [l, r] [r + 1, right]
if (k <= right - r)
return topK(nums, r + 1, right, k);
else if (k <= right - l + 1)
return key;
else
return topK(nums, left, l - 1, k - (right - l + 1));
}
347. 前 K 个高频元素
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]
示例 2:
输入: nums = [1], k = 1 输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的**进阶:**你所设计算法的时间复杂度 必须 优于
O(n log n)
,其中n
是数组大小。
优先级队列 + 哈希
cpp
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> hash;
for (int i : nums) {
hash[i]++;
}
auto cmp = [](pair<int, int>& a, pair<int, int>& b) {
return a.second > b.second;
};
// 小根堆
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> que;
for (auto& [num, count] : hash) {
que.emplace(num, count);
if (que.size() > k) {
que.pop();
}
}
vector<int> ans;
while (!que.empty()) {
ans.push_back(que.top().first);
que.pop();
}
return ans;
}
快速排序
cpp
void quickSort(vector<pair<int, int>>& v, int left, int right, int k) {
if (left >= right) {
return;
}
int key = v[rand() % (right - left + 1) + left].second;
int i = left, l = left, r = right;
while (i <= r) {
if (v[i].second < key) {
swap(v[i++], v[l++]);
} else if (v[i].second > key) {
swap(v[i], v[r--]);
} else {
i++;
}
}
// [left, l - 1] [l, r] [r + 1, right]
if (k <= right - r) {
quickSort(v, r + 1, right, k);
} else if (k <= right - l + 1) {
return;
} else {
quickSort(v, left, l - 1, k - (right - l + 1));
}
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> hash;
for (int i : nums) {
hash[i]++;
}
vector<pair<int, int>> v;
for (auto& p : hash) {
v.push_back(p);
}
srand(time(nullptr));
quickSort(v, 0, v.size() - 1, k);
vector<int> ans;
int i = v.size() - 1;
while (k--) {
ans.push_back(v[i--].first);
}
return ans;
}
295. 数据流的中位数
中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
arr = [2,3,4]
的中位数是3
。- 例如
arr = [2,3]
的中位数是(2 + 3) / 2 = 2.5
。实现 MedianFinder 类:
MedianFinder()
初始化MedianFinder
对象。void addNum(int num)
将数据流中的整数num
添加到数据结构中。double findMedian()
返回到目前为止所有元素的中位数。与实际答案相差10-5
以内的答案将被接受。示例 1:
输入 ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"] [[], [1], [2], [], [3], []] 输出 [null, null, null, 1.5, null, 2.0] 解释 MedianFinder medianFinder = new MedianFinder(); medianFinder.addNum(1); // arr = [1] medianFinder.addNum(2); // arr = [1, 2] medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2) medianFinder.addNum(3); // arr[1, 2, 3] medianFinder.findMedian(); // return 2.0
提示:
-10^5 <= num <= 10^5
- 在调用
findMedian
之前,数据结构中至少有一个元素- 最多
5 * 10^4
次调用addNum
和findMedian
que_min
是大根堆,用于存储数据流中较小的一半元素,堆顶元素是较小的一半元素中最大的元素。que_max
是小根堆,用于存储数据流中较大的一半元素,堆顶元素是较大的一半元素中最小的元素。
cpp
priority_queue<int, vector<int>, less<int>> que_min; // 大根堆
priority_queue<int, vector<int>, greater<int>> que_max; // 小根堆
MedianFinder() {}
void addNum(int num) {
if (que_min.empty() || num <= que_min.top()) {
que_min.push(num);
if (que_min.size() > que_max.size() + 1) {
que_max.push(que_min.top());
que_min.pop();
}
} else {
que_max.push(num);
if (que_max.size() > que_min.size()) {
que_min.push(que_max.top());
que_max.pop();
}
}
}
double findMedian() {
if (que_min.size() > que_max.size())
return que_min.top();
return (que_min.top() + que_max.top()) / 2.0;
}