【Data Structure】堆与优先队列

本文介绍一下重要的数据结构:堆,并以C++实现了整数最小堆。

堆是一种完全二叉树,特点是其根大于/小于其儿子,并且这个特性是递归定义的。这样的数据结构用于实现logN复杂度下进行一次最值得查询。



上图显示了一个最小堆。由于堆是完全二叉树,所以底层的数据结构用数组就可以了(当然C++里用vector更好)。那么,我们应该使堆具有哪些操作呢?首先我们应该可以实现对元素的插入。假设我们已经有一个堆,如何插入一个元素且保持对的性质呢?

首先我们写一个Heap类,底层用一个vector保存数据,把data[0]设为哨兵,这样有利于下标的操作。加入的时候,先把数据扔到数组尾部,然后再逐步往上(沿着父节点)直到调整到合适的位置。


上图实现了14的插入,具体代码如下:

void push(int temp)
    {
        data.push_back(temp);
        int cur=data.size()-1;
        while(cur!=1)
        {
            int father=cur/2;
            if(data[cur]<data[father])
            {
                int t=data[cur];
                data[cur]=data[father];
                data[father]=t;
            }
            cur=father;
        }
    }
下面我们考虑如何删除并输出最小值。显而易见,最小值就是data[1]。此时,我们把最尾部的数甩到根上,再逐步往下移动到合适位置。



由上图,我们此时应该对31进行percolate down,具体实现(包括pop)如下:

    void pop()
    {
        cout<<data[1]<<endl;
        data[1]=data[data.size()-1];
        data.pop_back();
        percolateDown(1);
    }
    void percolateDown(int hole)
    {
        int child;
        int tmp=data[hole];
        for(;hole*2<data.size();hole=child)//中间判断是不是叶子,最后更新hole(当然也可以写在循环中)
        {
            child=hole*2;//left child
            if(child!=data.size()-1&&data[child+1]<data[child])//if there is a right child and less then the left one
                child++;
            if(data[child]<tmp)//less than child,then swap
                data[hole]=data[child];
            else
                break;
        }
        data[hole]=tmp;//补上hole,此时hole显然已经变了
    }

最后,我们还要想办法实现把乱序数组初始化为堆。其实我们只要对数组的前一半用percolate down就好了。

另外,优先队列其实就是堆的一种外包装,pop操作弹出的事最值,这是利用堆来实现的,下面是heap类的完整代码(省去了析构函数),实际上就可以当做优先队列来用了。

class Heap
{
private:
    vector<int> data;
public:
    Heap(){data.push_back(-1);}
    Heap(const vector<int>& item)
    {
        data.push_back(-1);
        for(int i=0;i<item.size();i++)
            data.push_back(item[i]);
    }
    void push(int temp)
    {
        data.push_back(temp);
        int cur=data.size()-1;
        while(cur!=1)
        {
            int father=cur/2;
            if(data[cur]<data[father])
            {
                int t=data[cur];
                data[cur]=data[father];
                data[father]=t;
            }
            cur=father;
        }
    }
    void pop()
    {
        cout<<data[1]<<endl;
        data[1]=data[data.size()-1];
        data.pop_back();
        percolateDown(1);
    }
    void percolateDown(int hole)
    {
        int child;
        int tmp=data[hole];
        for(;hole*2<data.size();hole=child)//中间判断是不是叶子,最后更新hole(当然也可以写在循环中)
        {
            child=hole*2;//left child
            if(child!=data.size()-1&&data[child+1]<data[child])//if there is a right child and less then the left one
                child++;
            if(data[child]<tmp)//less than child,then swap
                data[hole]=data[child];
            else
                break;
        }
        data[hole]=tmp;//补上hole,此时hole显然已经变了
    }
    bool empty()
    {
        return !(data.size()-1);
    }
    void print()
    {
        for(int i=1;i<data.size();i++)
            cout<<data[i]<<' ';
        cout<<endl;
    }
    void build_heap()
    {
        for(int i=(data.size()-1)/2;i>=1;i--)
        {
            percolateDown(i);
        }
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值