数据结构:经典二叉堆

堆的性质:

1.根节点比左右叶子节点大(小)
2.完全二叉树,最后一层都在左侧

堆的存储

堆可以用数组存储

c++实现一个大顶堆

#include <algorithm>
#include <cassert>

using namespace std;


template<typename Item>
class MaxHeap{

private:
    Item *data;
    int count;
    int capacity;
    

    void shiftUp(int k){
        while( k > 1 && data[k/2] < data[k] ){
            swap( data[k/2], data[k] );
            k /= 2;
        }
    }

    void shiftDown(int k){
        while( 2*k <= count ){
            int j = 2*k;
            if( j+1 <= count && data[j+1] > data[j] ) j ++;
            if( data[k] >= data[j] ) break;
            swap( data[k] , data[j] );
            k = j;
        }
    }

public:

    MaxHeap(int capacity){
        data = new Item[capacity+1];
        count = 0;
        this->capacity = capacity;
    }

    MaxHeap(Item arr[], int n){
        data = new Item[n+1];
        capacity = n;

        for( int i = 0 ; i < n ; i ++ )
            data[i+1] = arr[i];
        count = n;

        for( int i = count/2 ; i >= 1 ; i -- )
            shiftDown(i);
    }

    ~MaxHeap(){
        delete[] data;
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }

    void insert(Item item){
        assert( count + 1 <= capacity );
        data[count+1] = item;
        shiftUp(count+1);
        count ++;
    }

    Item extractMax(){
        assert( count > 0 );
        Item ret = data[1];
        swap( data[1] , data[count] );
        count --;
        shiftDown(1);
        return ret;
    }

    Item getMax(){
        assert( count > 0 );
        return data[1];
    }
};

构建最大堆的方法:

1.将n个元素逐个插入到一个空堆中,算法复杂度是O(nlogn)
每次插入都要构建一次最大堆,
每次和父节点比较,若大于父节点,交换位置,还要防止下标越界
也就是 "shift up"
2.heapify的过程,算法复杂度为O(n)
从最后一个非叶子节点的父节点开始,
相当于减少了一半的元素
也就是 "shift down"

堆应用

1.优先级队列
普通队列一般都是先进先出,优先级队列后进,但有可能先出
比如说,医院里排队看病,如果遇到有急诊病人的话,可以在队列的前面优先看病。还有机场排队过安检,如果有乘客飞机即将起飞了,一般机场会有紧急安检通道,可以让比较着急的乘客优先过安检。
优先队列可以构造一个大顶堆,新插入的元素来维持这个大顶堆,每次从根节点取出元素。
2.从10亿个数中取出前100个最大的数
若是这10亿个数,用快速排序,nlogn的时间复杂的,
可是10亿个数,如果一个数占4byte的话,10亿个数大约快4G,
如果计算机内存2G的话,一次读取时放不下的。
可以在内存中维护100个数的小顶堆,若是新来的元素比对顶元素小,直接放弃继续从剩余元素中查找,如果比对堆顶大,则替换堆顶元素,可以用shift down方法在构建一个小顶堆。
此时算法的时间复杂度是nlog(m),也就是 10亿*log(100)
3.多路归并排序
一般归并排序,将数组分为两份,多路含义可以将数组分为多份,d份,
d越大,归并的层级也就越低,到时每次比较d个数,时间复杂的是O(d),
如果这个d个数构造一个小顶堆来比较,将堆顶元素取出后,在相应的路上,后移动取出下一个元素,再构造一个小顶堆,这个时间复杂度就是O(dlog(d))

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 堆就是用数组实现的二叉树,所以它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。...
    唐先僧阅读 249,608评论 21 252
  • 什么是堆,堆的特点 一棵完全二叉树,且满足任意节点始终不大于(或者不小于)左右子节点。前者称为最小堆,后者为最大堆...
    萌妈码码阅读 263评论 0 0
  • 简介 堆是一种基于完全二叉树的数据结构.完全二叉树: 每个节点最多有两个子节点(二叉)除了最底层, 其他每一层都必...
    是杨不是阳羊扬阅读 3,392评论 0 4
  • 今天,我们来谈一谈一个很常用的数据结构---堆。堆(Heap),也叫优先队列(Priority Queue)。是一...
    小黑_328a阅读 1,684评论 0 0
  • 堆(heap)又被为优先队列(priority queue)。尽管名为优先队列,但堆并不是队列。回忆一下,在队列中...
    jacky123阅读 392评论 1 2