1. 无序数组找最大的K个数
方法1: 排序+查找 时间复杂度O(NlogN)
int findKthLargest(vector<int> & nums, int k) {
sort(nums.begin(), nums.end());
return nums[k-1];
}
方法2: 类似快排的分治思想 O(N)
int partition(vector<int> &nums, int left, int right) {
int l = left, r = right;
int pivot = nums[l];
while (l < r) {
while (l < r && nums[r] <= pivot)
r--;
nums[l] = nums[l];
while (l < r && nums[l] >= pivot)
l++;
nums[r] = nums[l];
}
nums[l] = pivot;
return l;
}
int findKthLargest_QuickSort(vector<int> nums, int k) {
int left = 0, right = nums.size() - 1;
while (true) {
int pos = partition(nums, left, right);
if (pos == k - 1) return nums[pos];
if (pos > k - 1) right = pos;
else left = pos;
}
}
方法3:堆排序思想,调整堆k次
void max_heapify(vector<int> &nums, int p, int heap_size) {
int child = 2 * p + 1;
int p_val = nums[p];
while (child < heap_size) {
if (child + 1 < heap_size && nums[child] < nums[child+1]) {
child += 1;
}
if (nums[child] > p_val) {
nums[p] = nums[child] ;
p = child;
child = 2 * p + 1;
} else
break;
}
nums[p] = p_val;
}
void build_max_heap(vector<int> &nums, int heap_size) {
int heap_size = nums.size();
for (int i = (heap_size >> 1) -1; i >= 0; i--) {
max_heapify(nums, i, heap_size);
}
}
int findKthLargest_Heap(vector<int> nums, int k) {
build_max_heap(nums, heap_size);
for (int i = 0; i < k; i++) {
swap(nums[0], nums[heap_size-1]);
heap_size --;
max_heapify(nums, 0);
}
return nums[heap_size];
}
方法4: 利用STL的priority_queue
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int> pq(nums.begin(), nums.end());
for (int i = 0; i < k - 1; i++) {
pq.pop();
}
return pq.top();
}
2. 无序数组找K最频繁(要求时间复杂度小于O(NlogN))
因为k最大可以等于n,所以在最坏情况下需要统计所有数字的出现次数。那么这个问题就分成了两个部分:
因为最后只需要返回k个数字,所以可以维护一个大小为k的小根堆。当新的数字出现的次数大于堆中最小的次数时,对堆进行更新。时间复杂度是O(n log k)。
- 统计所有不同的数字出现的次数
- 找出出现次数前k大的数字
// hash + priority_queue
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> map;
for(int num : nums){
map[num]++;
}
vector<int> res;
// pair<first, second>: first is frequency, second is number
priority_queue<pair<int,int>> pq;
for(auto it = map.begin(); it != map.end(); it++){
pq.push(make_pair(it->second, it->first));
if(pq.size() > (int)map.size() - k){
res.push_back(pq.top().second);
pq.pop();
}
}
return res;
}
可以采用桶排序做进一步优化,该算法复杂度只和需要排序的数字的大小有关。可以用一个数组bucket记录每个数字出现的次数,每次把数字丢到相应编号的桶中,然后从后往前穷举每一个桶,取出其中的元素直到取满k个。时间复杂度是O(n)。
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> res;
if (!nums.size()) return res;
unordered_map<int, int> cnt;
for (auto num : nums) cnt[num]++;
vector<vector<int>> bucket(nums.size() + 1);
for (auto kv : cnt) {
bucket[kv.second].push_back(kv.first);
}
for (int i = bucket.size() - 1; i >= 0; --i) {
for (int j = 0; j < bucket[i].size(); ++j){
res.push_back(bucket[i][j]);
if (res.size() == k) return res;
}
}
return res;
}