zoukankan      html  css  js  c++  java
  • 【学习笔记】不会吧不会吧,不会有人还在手写堆吧

    今天小编在水昨天考试题题解的时候,突然发现了 STL 自带的手写堆函数,本来以为优先队列已经够了,但是没想到这些函数居然折磨好用,下面快和小编一起来看一看吧~

    前言

    这些函数位于 <functional> 头文件中,这个头文件好像被 <algorithm> 包含了,不过当然 <bits/stdc++.h> 也是没问题的。

    除了普通的数组,结构体建堆也是没有问题的,但需要重载运算符。

    以下的数组均以 q[0] 储存堆大小。

    建堆

    函数原型: make_heap(_RAIter,_RAIter,_Compare)

    该函数用于把一个可迭代容器变成一个满足堆性质的序列,默认是大顶堆。时间复杂度为从下向上建堆的 (O(n))

    它有三个参数,前两个是容器的开始元素迭代器和结束元素迭代器(同样是左闭右开)。第三个参数用于生成小根或大根堆,分别是 greater<>()less()<>,当然也可以是自定义的比较函数。默认是大根堆。

    注意,如果使用了第三个参数,以下其他所有函数都需要使用同样的参数。

    for(int i=1;i<=5;i++)
        q[++q[0]]=i;
    make_heap(q+1,q+q[0]+1);
    for(int i=1;i<=5;i++)
        printf("%d ",q[i]);
    

    OUTPUT:
    5 4 3 1 2

    删除

    函数原型:pop_heap(_RAIter,_RAIter,_Compare)

    参数意义同上。该函数用于将堆的第一个元素与最后一个元素交换位置,然后针对前 (n-1) 个元素调用 make_heap() 函数,看似好像比较慢,但实际上最坏的时间复杂度约是 (O(2log n))

    注意本函数只是将元素交换位置,所以真正删除需要 q[0]--vector 的话就是 pop_back()

    for(int i=1;i<=5;i++)
        printf("%d ",q[i]);
    puts("");
    pop_heap(q+1,q+q[0]+1);
    for(int i=1;i<=5;i++)
        printf("%d ",q[i]);
    puts("");
    

    OUTPUT:
    5 4 3 1 2
    4 2 3 1 5

    插入

    函数原型:push_heap(_RAIter,_RAIter,_Compare)

    参数意义同上。用于把数据插入到堆中。时间复杂度最坏 (O(log n))

    在使用该函数之前,请确保要插入的数据被加入到容器末尾,如 vector 就是 push_back()

    for(int i=1;i<=5;i++){
        q[++q[0]]=i;
        push_heap(q+1,q+q[0]+1);
    }
    for(int i=1;i<=5;i++)
        printf("%d ",q[i]);
    

    OUTPUT:
    5 4 2 1 3

    当然,先 make_heap 之后再 push_heap 也是可以的,这里不再赘述。

    堆排序

    函数原型:sort_heap(_RAIter,_RAIter,_Compare)

    参数意义同上。此函数会将满足堆性质的序列依据大根堆或小根堆排序,排序后,序列将失去堆的性质。时间复杂度 (O(nlog n))

    注意大根堆将变成一个递增序列,小根堆将变成一个递减序列。

    请确保在排序前序列满足堆的性质,否则会报错。

    不过这个用的应该不多,不如直接 sort 呢。

    for(int i=1;i<=5;i++)
        q[++q[0]]=i;
    make_heap(q+1,q+q[0]+1);
    for(int i=1;i<=5;i++)
        printf("%d ",q[i]);
    puts("");
    sort_heap(q+1,q+q[0]+1);
    for(int i=1;i<=5;i++)
        printf("%d ",q[i]);
    puts("");
    

    OUTPUT:
    5 4 3 1 2
    1 2 3 4 5

    我还是想用优先队列 哭哭

    这两个代码分别用优先队列和这些函数插入和删除 (10^6) 个元素。

    优先队列:

    priority_queue<int> q;
    for(int i=1;i<=1e6;i++)
        q.push(i);
    for(int i=1;i<=1e6;i++)
        q.pop();
    

    数组:

    for(int i=1;i<=1e6;i++){
        q[++q[0]]=i;
        push_heap(q+1,q+q[0]+1);
    }
    for(int i=1;i<=1e6;i++){
        pop_heap(q+1,q+q[0]+1);
        q[0]--;
    }
    

    大家都能看出来区别。

    所以,在没有增加太多代码量的同时,我们做到了手写堆的效果,小编不禁直呼爷爱了。

    总结

    所以,如果有优先队列卡常卡不过去的情况下还不想手写堆,可以试试这些函数。

    当然,在开启 O2 优化的情况下,两者的差距还是不大的,毕竟理论时间复杂度是一样的。

    最后来个封装好 pushpop 的“手写“大根堆:

    struct PRIORITY_QUEUE{
        int q[maxn];
        inline void Push(int x){
            q[++q[0]]=x;
            push_heap(q+1,q+q[0]+1);
        }
        inline void Pop(){
            pop_heap(q+1,q+q[0]+1);
            q[0]--;
        }
        inline int Top(){
        	return q[1];
        }
        inline int Size(){
            return q[0];
        }
        inline bool Empty(){
            return !q[0];
        }
    }q;
    

    来波实测

  • 相关阅读:
    输入adb shell 时 提示error: more than one device and emulator
    增加 addDataScheme("file") 才能收到SD卡插拔事件的原因分析 -- 浅析android事件过滤策略
    Android音乐编程:管理音频焦点
    Android MediaScanner 详尽分析
    MediaScanner与音乐信息扫描==
    Vold工作流程分析学习
    [LeetCode] Majority Element II
    Android 四大组件学习之ContentProvider五
    HDU 1171 Big Event in HDU(01背包)
    Atom编辑器折腾记_(15)JS代码片段补全(插件:javascript-snippets)
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13831937.html
Copyright © 2011-2022 走看看