题目描述:在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
解题思路:对数组进行排序,返回第K个最大的元素。
(1)可以使用排序函数sort,时间复杂度 O(NlogN),空间复杂度 O(1)
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int n = nums.size();
sort(nums.begin(),nums.end());
return nums[n-k];
}
};
(2)使用堆排序,时间复杂度 O(NlogK),空间复杂度 O(K)。topk (前k大)用小根堆,维护堆大小不超过 k 即可。每次压入堆前和堆顶元素比较,如果比堆顶元素还小,直接扔掉,否则压入堆。检查堆大小是否超过 k,如果超过,弹出堆顶。求前 k 大,用小根堆,求前 k 小,用大根堆。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int n = nums.size();
for (int i = n / 2 - 1; i >= 0; i--)//生成初始堆
adjust(nums,i,n);
for (int j = n-1; j >= n-k; j--){//循环k次,可以找到最大的k个数并排序
int tmp = nums[0];
nums[0] = nums[j];
nums[j] = tmp;
adjust(nums,0,j);
}
return nums[n-k];
}
private:
void adjust(vector<int>& nums, int i, int n){//调整以节点i为根的二叉树
int j = 2*i + 1;
while (j < n){
if (j+1 < n && nums[j] < nums[j+1])
j++;//j指向两个子节点中较大的
if (nums[i] < nums[j]){//需要交换
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
i = j;
j = 2 * i + 1;
}
else//不用交换
break;
}
}
};
使用STL中的priority_queue来创建堆。greater< int >创建的是小根堆,less< int> 创建的是大根堆。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int,vector<int>,greater<int>> heap;
for (int num : nums){
if (heap.size() < k)
heap.push(num);
else if (num > heap.top()){
heap.pop();
heap.push(num);
}
}
return heap.top();
}
};
(3)使用快速排序。从大到小排序,如果k-1位置上的元素归位,则找到了第k大的元素。如果归位的元素在k-1的右侧,则接着对该归位元素的左侧区间进行操作;如果归位元素在k-1的左侧,则对右侧区间继续操作。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int l = 0, r = nums.size()-1;
while (l < r){
int j = MyPartition(nums,l,r);
if (j == k - 1)
break;
else if (j < k - 1)
l = j + 1;
else
r = j - 1;
}
return nums[k-1];
}
private:
int MyPartition(vector<int>& nums, int l, int r){
random_shuffle(nums.begin()+l, nums.begin()+r+1);
int i = l, j = r;
int tmp = nums[i];
while (i < j){//将大于nums[i]的放在左侧区间,小于的放在右侧区间
while(i < j && nums[j] <= tmp)
j--;
nums[i] = nums[j];
while (i < j && nums[i] >= tmp)
i++;
nums[j] = nums[i];
}
nums[j] = tmp;
return j;
}
};
题目描述:给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
解题思路:遍历一次数组,使用unordered_map记录各个数字及其出现的次数。建立一个大小为k的小根堆,保存出现频率最多的k个元素,这里往堆中添加元素,如果堆的规模大于k,则将堆顶元素弹出,每次弹出的都是当前堆中的最小值。最后将这些元素加入到结果数组中。
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> res;
unordered_map<int,int> mp;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
for (auto i : nums)
mp[i]++;//将键值i对应的值加1
for (auto p : mp){
pq.push(pair<int,int>(p.second,p.first));
if (pq.size() > k) pq.pop();
}
while (k--){
res.push_back(pq.top().second);//pq元素的second对应于p元素的first,表示键
pq.pop();
}
return res;
}
};
题目描述:给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
解题思路:使用unordered_map将字母与出现的次数对应着存储,然后建立大根堆进行排序。
class Solution {
public:
string frequencySort(string s) {
string res;
unordered_map<char,int> mp;
priority_queue<pair<int,char>,vector<pair<int,char>>,less<pair<int,char>>> pq;
for (auto c : s)
mp[c]++;
for (auto p : mp){
pq.push(pair<int,char>(p.second,p.first));
}
while (!pq.empty()){
for (int i = 0; i < pq.top().first; i++)
res.push_back(pq.top().second);
pq.pop();
}
return res;
}
};
题目描述:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
输入: [2,0,2,1,1,0];输出: [0,0,1,1,2,2]
解题思路:
初始化0的最右边界:p0 = 0。在整个算法执行过程中 nums[idx < p0] = 0. 即p0左端的都为0
初始化2的最左边界 :p2 = n - 1。在整个算法执行过程中 nums[idx > p2] = 2. 即p2右端的都为2.
初始化当前考虑的元素序号 :curr = 0.
While curr <= p2 :
若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针都向右移。
若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。
若 nums[curr] = 1 :将指针curr右移。
class Solution {
public:
void swap(vector<int>& nums, int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
void sortColors(vector<int>& nums) {
int zero = 0, one = 0, two = nums.size() - 1;
while (one <= two){
if (nums[one] == 2){
swap(nums,one,two--);//two指针左移
}
else if (nums[one] == 0)
swap(nums,one++,zero++);//one、zero指针都右移
else
one++;//one指针右移
}
}
};