算法------(10)堆

例题:(1)AcWing 838. 堆排序

算法------(10)堆_第1张图片

        我们可以利用一个一维数组来模拟堆。由于堆本质上是一个完全二叉树,他的每个父节点的权值都小于左右子节点,而每个父节点编号为n时,左节点编号为2*n,右节点编号为2*n+1。用size记录堆的大小便于维护。在初始建立堆时,由于最后一层在前面每一层建立后自然而然就会被排序,因此我们只需要从倒数第二层开始建立到第一层即可。建立的过程利用到down操作,down操作本质上就是在父节点和两个子节点中进行比较换位,直到往下也不需要换位。而由于一维数组不方便删除头结点,但是删除尾节点只需要size--,因此我们删除头节点的操作就是把头结点和尾结点交换,然后把新的头节点down。 

#include 
#include 
#include 
using namespace std;
const int N = 100010;
int heap[N],sz;
void down(int u){
    int t = u;
    if(u*2<=sz&&heap[u*2] < heap[t]) t = u*2;
    if(u*2+1<=sz&&heap[u*2+1] < heap[t]) t = u*2+1;
    if(t!=u){
        swap(heap[t],heap[u]);
        down(t);
    }
}
int main()
{
    int n,m;
    scanf("%d%d", &n, &m);
    for(int i = 1;i<=n;i++){
        scanf("%d", &heap[i]);
    }
    sz = n;
    for(int i = n/2;i;i--){
        down(i);
    }
    while(m--){
        printf("%d ",heap[1]);
        heap[1] = heap[sz--];
        down(1);
    }
    return 0;
}

(2)AcWing 839. 模拟堆  

算法------(10)堆_第2张图片

        模拟堆的过程相比上面多了几个操作:(1)删除第k个数,修改第k个插入的数,需要用一个记录下标-第k个插入的数和相反关系的两个数组进行维护。(2)还需要用up把下面的较小数往上移。 

#include 
#include 
#include 
using namespace std;
const int N = 1e5+10;
unordered_map id,mp;//id的对应关系:下标-第k个插入的数,mp的对应关系: 第k个插入的数 - 下标 
int h[N];
int sz,cnt;
void down(int u){
    int t = u;
    if(u*2<=sz&&h[u*2]h[t]) t = u/2;
    if(t!=u){
        swap(mp[id[u/2]],mp[id[u]]);
        swap(id[u/2],id[u]);
        swap(h[u/2],h[u]);
        up(t);
    }
}
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 0;i

练习:(1)Leetcode 692.前k个高频单词算法------(10)堆_第3张图片         没做出来。。优先队列居然还能这么用的吗。。

         先用哈希表统计每个单词出现的次数,然后遍历哈希表(可以用auto直接循环),把每个加入到优先队列进行排序。最后由于需要按照字典序排序,我们还需要从后往前放置答案。

class Solution {
public:
    vector topKFrequent(vector& words, int k) {
        unordered_map x;
        for(auto c:words){
            x[c]++;
        }

        auto cmp = [](const pair &a,pair &b){
            return a.second == b.second ? a.first < b.first : a.second > b.second;
        };
        priority_queue,vector>, decltype(cmp)> ans(cmp);
        for(auto c:x){
            ans.push(c);
            if(ans.size()>k) ans.pop();
        }
        vector res(k);
        for(int i = k-1;i>=0;i--){
            res[i]=ans.top().first;
            ans.pop();
        }
        return res;
    }
};

(2)Leettcode 703.数据流中的第K大元素算法------(10)堆_第4张图片 

        一开始一直想靠sort。。没用。。

        由于优先队列只能访问队头数组,所以我们要让数据流中的第K大元素是优先队列的队头,因此我们要保证优先队列中只有K个数,这K个数中的最小的数就一定是第K大的数,也是优先队列的队头。

class KthLargest {
public:
    priority_queue,greater> x;
    int k;
    KthLargest(int k, vector& nums) {
        this->k = k;
        for(auto c:nums){
            x.push(c);
            if(x.size()>k) x.pop();
        }
    }
    
    int add(int val) {
        x.push(val);
        if(x.size()>k) x.pop();
        return x.top();
    }
};

(3) Leetcode 264.丑数II

        算法------(10)堆_第5张图片

        没做出来。。

        假如一个数为丑数,那么这个数的两倍,三倍和五倍对应的数也是丑数,当然这么做肯定会有重复,因此我们用一个哈希集合来存储出现过的数,这样保证小根堆里面不出现重复的数,因此第k次取出来的数就是第k个丑数。

class Solution {
public:
    long nthUglyNumber(int n) {
        priority_queue,greater> x;
        unordered_set y;
        x.push(1);
        y.insert(1);
        for(int i = 0;i

你可能感兴趣的:(算法基础课,算法,c++,数据结构)