LeetCode 热题 100 堆
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)
大的数。
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];// 随机获取keyint 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]);elsei++;}// [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;elsereturn 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)
大的数。
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];// 随机获取keyint 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]);elsei++;}// [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;elsereturn 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
是数组大小。
优先级队列 + 哈希
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;
}
快速排序
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
是小根堆,用于存储数据流中较大的一半元素,堆顶元素是较大的一半元素中最小的元素。
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;
}