堆的基础知识
可以把堆看作一个数组,也可以被看作一个完全二叉树,通俗来讲堆其实就是利用完全二叉树的结构来维护的一维数组
- 大顶堆:每个结点的值都大于或等于其左右孩子结点的值
- 小顶堆:每个结点的值都小于或等于其左右孩子结点的值
堆排序的口诀
- 将堆顶元素与堆尾元素交换
- 将此操作看作是堆顶元素弹出操作
- 按照头部弹出以后的策略调整堆
堆-优先队列
普通队列 | (最大/最小)堆 |
---|---|
尾部入队 | 尾部可以插入 |
头部出队列 | 头部可以弹出 |
先进先出 | 每次出队权值(最大/最小的元素) |
数组实现 | 数组实现,逻辑上看成一个堆 |
实现
#define MAX_N 1000;
int data[MAX_N + 5], cnt = 0;
void shift_up(int ind){
while(ind && data[(ind - 1)/2] < data[ind]){
swap(data[(ind - 1)/2], data[ind]);
ind = (ind - 1)/2;
}
return;
}
void shift_down(int ind){
int n = cnt - 1;
while(ind * 2 + 1 <= n){
int tamp = ind;
if(data[temp] < data[ind * 2]+1) temp = ind * 2 + 1;
if(ind * 2 + 2 <= n && data[temp] < data[ind * 2 + 2]) temp = ind * 2 + 2;
if(temp == ind) break;
swap(data[temp], data[ind]);
ind = temp;
}
return
}
void push(int x){
data[cnt++] = x;
shift_up(cnt - 1);
return;
}
void pop(){
if(size() == 0) return;
swap(data[0], data[cnt - 1]);
cnt -= 1;
shift_down(0);
return;
}
int top(){
return data[0];
}
int size(){
return cnt;
}
int main(){
int op, val;
while(cin >> op){
switch(op){
case 0: cin>>val; printf("push %d to head \n"); push(val); break;
case 1: printf("pop %d from heap \n", top()); pop(); break;
}
}
return 0;
}
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
一句话理解堆
堆适合维护:集合最值
经典面试题 - 堆的基础应用
剑指 Offer 40. 最小的k个数
class Solution {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
vector<int> getLeastNumbers(vector<int>& arr, int k) {
Heap<int> h{less<int>()};
for(auto x: arr){
h.push(x);
if(h.size() > k) h.pop();
}
return h;
}
};
1046. 最后一块石头的重量
class Solution {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
int lastStoneWeight(vector<int>& stones) {
Heap<int> h{less<int>()};
for(auto x: stones){
h.push(x);
}
while(h.size() > 1){
int y = h.top(); h.pop();
int x = h.top(); h.pop();
if(x == y) continue;
h.push(y - x);
}
if(h.size() == 0) return 0;
return h.top();
}
};
703.数据流中的第k大元素
class KthLargest {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
Heap<int> h{greater<int>()};
int k;
KthLargest(int k, vector<int>& nums) :k(k) {
for(auto x: nums){
add(x);
}
return;
}
int add(int val) {
h.push(val);
if(h.size() > k) h.pop();
return h.top();
}
};
215.数组中的第k个最大元素
class Solution {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
int findKthLargest(vector<int>& nums, int k) {
Heap<int> h{greater<int>()};
for(auto x: nums){
h.push(x);
if(h.size() > k) h.pop();
}
return h.top();
}
};
692. 前K个高频单词
class Solution {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
vector<string> topKFrequent(vector<string>& words, int k) {
unordered_map<string, int> freq;
for(auto x: words) freq[x] += 1;
auto cmp = [&freq](string a, string b) ->bool {
if(freq[a] - freq[b]) return freq[a] > freq[b];
return a < b;
};
Heap<string> h{cmp};
for(auto x: freq){
h.push(x.first);
if(h.size() > k) h.pop();
}
sort(h.begin(), h.end(), cmp);
return h;
}
};
296.数据流的中位数
对顶堆:将数据分为两半,前半段的最后一个是前半段的最大值,后半段的第一个是后半段的最小值,演变成一个小顶堆和一个大顶堆的管理
class MedianFinder {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
T pop(){
T ret = top();
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ret;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
Heap<int> h1, h2;
MedianFinder() : h1{less<int>()}, h2{greater<int>()}{}
void addNum(int num) {
if(h1.size() == 0 || num <= h1.top()){
h1.push(num);
}else{
h2.push(num);
}
if(h2.size() > h1.size()){
h1.push(h2.pop());
}
if(h1.size() == h2.size() + 2){
h2.push(h1.pop());
}
return;
}
double findMedian() {
int n = h1.size() + h2.size();
if(n %2 == 1){
return h1.top();
}
return 1.0 * (h1.top() + h2.top()) / 2;
}
};
264.丑数ii
class Solution {
public:
template<typename T>
class Heap : public vector<T>{
public:
template<typename Func_T>
Heap(Func_T cmp) : cmp(cmp) {}
void push(const T &a){
this->push_back(a);
push_heap(this->begin(), this->end(), cmp);
return ;
}
void pop(){
pop_heap(this->begin(), this->end(), cmp);
this->pop_back();
return ;
}
T &top() {return this->at(0); }
private:
function<bool(T, T)> cmp;
};
int nthUglyNumber(int n) {
Heap<long long> h{greater<long long>()};
long long ans = 0;
h.push(1);
while(n--){
ans = h.top();
h.pop();
if(ans % 5 == 0){
h.push(ans * 5);
}else if(ans % 3 == 0){
h.push(ans * 5);
h.push(ans * 3);
}else{
h.push(ans * 5);
h.push(ans * 3);
h.push(ans * 2);
}
}
return ans;
}
};
1753.移除石子的最大得分
class Solution {
public:
int maximumScore(int a, int b, int c) {
if (a > b) swap(a, b);
if (a > c) swap(a, c);
if (b > c) swap(b, c);
int ans = 0;
// step1
int cnt1 = min(c - b, a);
a -= cnt1;
c -= cnt1;
ans += cnt1;
// step2
if(a != 0){
if(a % 2 == 1) a -= 1;
b -= a / 2;
c -= b / 2;
ans += a;
}
// step3
ans += b;
return ans;
}
};