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;
}