面试题 17.14. 最小K个数
题目
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]
提示:0 <= len(arr) <= 100000
0 <= k <= min(100000, len(arr))
class Solution {
public:
vector<int> smallestK(vector<int>& arr, int k) {
int len = arr.size();
if (len <= k)
return arr;
vector<int> res;
priority_queue<int> maxHeap;
for (int i = 0; i < len; ++i) {
maxHeap.push(arr[i]);
if (maxHeap.size() > k)
maxHeap.pop();
}
while (!maxHeap.empty()) {
res.push_back(maxHeap.top());
maxHeap.pop();
}
return res;
}
};
347. 前 K 个高频元素
题目
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:输入: nums = [1], k = 1
输出: [1]提示:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案。
解法:
class Solution {
public:
#define PAIR pair<int, int> //简写类型
class cmp { //重写比较函数,小堆顶
public: //不可省略
bool operator()(const PAIR& left, const PAIR& right) {
return left.second > right.second;
}
}; //标点符号
vector<int> topKFrequent(vector<int>& nums, int k) {
int len = nums.size();
if (k == 0)
return vector<int>();
if (k == len)
return nums;
unordered_map<int, int> map; //用hash计数
for (int i = 0; i < len; ++i)
++map[nums[i]];
priority_queue<PAIR, vector<PAIR>, cmp> heap; //用堆对计数的结果进行排序,三个参数
for (auto it = map.begin(); it != map.end(); ++it) {
heap.push(*it);
if (heap.size() > k)
heap.pop();
}
vector<int> res(k); //存入结果
for (int i = 0; i < k; ++i) {
res[i] = heap.top().first;
heap.pop();
}
return res;
}
};
注意cmp的写法,是>,即小堆,先把小数pop掉。
附
priority_queue
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认是vector),Functional 就是比较的方式。
- 插入:先添加到尾部,然后不断与父节点比较,不断上升。
- 删除:先将根节点与最后一个节点交换位置,然后将最后一个节点(即原来的根节点)弹出
当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。没有迭代器,不允许遍历。
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。
和队列基本操作相同:
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
//升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列,大顶堆,默认
priority_queue <int,vector<int>,less<int> >q;
/// heap默认为大堆,以下设置为建立小堆
template <typename T>
struct greator
{
bool operator()(const T &x, const T &y)
{
return x > y;
}
};
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
1:用pair做优先队列元素的例子:
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
priority_queue<pair<int, int> > a;
pair<int, int> b(1, 2);
pair<int, int> c(1, 3);
pair<int, int> d(2, 5);
a.push(d);
a.push(c);
a.push(b);
while (!a.empty())
{
cout << a.top().first << ' ' << a.top().second << '\n';
a.pop();
}
}
:2:用自定义类型做优先队列元素的例子
#include <iostream>
#include <queue>
using namespace std;
//方法1
struct tmp1 //运算符重载<
{
int x;
tmp1(int a) {x = a;}
bool operator<(const tmp1& a) const
{
return x < a.x; //大顶堆
}
};
//方法2
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
}
};
int main()
{
tmp1 a(1);
tmp1 b(2);
tmp1 c(3);
priority_queue<tmp1> d;
d.push(b);
d.push(c);
d.push(a);
while (!d.empty())
{
cout << d.top().x << '\n';
d.pop();
}
cout << endl;
priority_queue<tmp1, vector<tmp1>, tmp2> f;
f.push(b);
f.push(c);
f.push(a);
while (!f.empty())
{
cout << f.top().x << '\n';
f.pop();
}
}