二叉堆的实现和详解(优先队列的基础)

11 篇文章 0 订阅

二叉堆的基本内容:

由于堆是一颗被完全填满的二叉树,所以最后一层是从左到右一次填入的,所以可以不必要链表表示(不连续),可以用数组表示比较节省开销(vector)。在堆操作中,两个比较重要和新的内容是上滤和下滤,个人总结了一下,插入新的结点的时候用上滤,删除最小结点的时候用下滤。还有一个值得注意的概念,它叫空穴,空穴顾名思义是空的,比如删除了根结点的元素,那么现在根结点即为空穴。好接下来我们就来看看最大堆和最小堆及下滤和上滤的概念和算法流程。

最大堆:

即根结点值最大,第二层的结点的值总是小于第一层结点的值,第三层总是小于第二层,依次类推,只要破坏这种关系,那就不叫最大堆了,会退化成普通的树。

最小堆:

即根结点值最小,第二层的结点的值总是大于第一层结点的值,第三层总是大于第二层,以此类推,只要破坏这种关系,那就不叫最小堆了,会退化成普通的树。`

下滤:

就下滤来说,在删除最小结点的时候(最小堆为根结点),首先我们先把堆中最后一个元素填进空穴中,即为根结点,然后开始执行下滤,从根结点出发,比较左右两个结点,如果是最小堆的话,那么选择左右结点中较小的那个,然后和当前空穴中的值比较,如果小于空穴里的值,那么就交换,否则空穴中的值(即堆中最后一个结点)就是停在当前这个位置了,下滤就结束了。

上滤:

就上滤而言,在插入的时候(由于插入的值)可能会破坏堆的结构,所以我们必须做出调整,把要插入的结点放入空穴当中,然后依次与父节点比较大小,如果是最小堆的话,那么,如果空穴值小于父节点,那么则与父节点交换位置,否则空穴就停在堆中的这个位置,上滤结束。

二叉堆的插入:

执行上滤。

BuildHeap:

建堆的时候,由于插入的时候可能是乱序的,所以要排成堆的性质,那就必须满足要码最大堆,要码最小堆,所以可以利用下滤来排序,比如有15个结点,那个取15/2 = 7开始执行下滤。分别执行percolateDown(7),percolateDown(6),percolateDown(5)......percolateDown(1)一直到根结点为止,这样建堆就满足了堆的性质。

DeleteMin:

建堆后,如果建立的是最小堆,直接删除根结点。然后执行下滤便可。

二叉堆代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
using namespace std;
class UnderflowException
{};
template<typename Comparable>
class BinaryHeap
{
public:
    explicit BinaryHeap(int capacity = 100);
    explicit BinaryHeap(const vector<Comparable> & items):array(items.size() + 10),currentSize(items.size())
    {
        for(int i=0;i<items.size();i++)
        {
            array[i+1] = items[i];
        }
        buildHeap();
    }
    bool isEmpty() const;
    const Comparable & findMin() const;
    void insert(const Comparable & x);
    void deleteMin();
    void deleteMin(Comparable & minItem);  //如果是最小堆的话,那么删除的就是根结点,然后置根结点为空穴,然后进行下滤
    void makeEmpty();
    void print();
private:
    int currentSize; //Number of elements in heap
    vector<Comparable> array; // 因为堆满足完全二叉树,所以可以用数组,因为数组是连续的,如果不连续要用链表(List)
    void buildHeap();
    void percolateDown(int hole); //下滤
};
template<typename Comparable>
void BinaryHeap<Comparable>::makeEmpty()
{
    currentSize = 0;
}
template<typename Comparable>
void BinaryHeap<Comparable>::print()
{
    for(int i=0;i<currentSize+1;i++)
    {
        if(array[i] != 0)
         cout<<array[i]<<" ";
    }
    cout<<endl;
}
template<typename Comparable>
bool BinaryHeap<Comparable>::isEmpty() const
{
    if(currentSize == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}
template<typename Comparable>
const Comparable &BinaryHeap<Comparable>::findMin() const
{
    return array[1];
}
template<typename Comparable>
void BinaryHeap<Comparable>::insert(const Comparable & x)
{
    if(currentSize == array.size() - 1) //满了
    {
        array.resize(array.size() * 2); //开辟两倍的空间
    }
    //上滤(percolate up)
    int hole = ++currentSize;
    for( ;hole > 1 && x < array[hole/2];hole /= 2) //如果要插入的结点值小于
    {
        array[hole] = array[hole / 2];
    }
    array[hole] = x;
}
template<typename Comparable>
void BinaryHeap<Comparable>::buildHeap() //堆排序
{
    for(int i=currentSize / 2;i>0;i--)
        percolateDown(i);
}
template<typename Comparable>
void BinaryHeap<Comparable>::deleteMin()
{
    if(isEmpty())
        throw UnderflowException();
    array[1] = array[currentSize--];  //最后一个结点赋值给根结点,然后执行下滤,根结点现在为空穴
    percolateDown(1);
}
template<typename Comparable>
void BinaryHeap<Comparable>::deleteMin(Comparable & minItem)
{
    if(isEmpty())
        throw UnderflowException();
    minItem = array[1];
    array[1] = array[currentSize--];
    percolateDown(1);
}
template<typename Comparable>
void BinaryHeap<Comparable>::percolateDown(int hole)
{
    int child;
    Comparable tmp = array[hole];
    for(;hole * 2 <= currentSize;hole = child) //每次往下访问一层
    {
        child = hole * 2;
        /*
            下面这个if的功能为比较左右结点的大小,把小的和空穴交换位置
        */
        if(child != currentSize && array[child + 1] < array[child]) //如果array[child + 1]比较小就访问右子树
        {
            child++;  //如果是右结点比较小,那么就访问右结点
        }
        if(array[child] < tmp) //如果当前访问的结点小于空穴的值,交换空穴和当前访问结点的位置
        {
            array[hole] = array[child];
        }
        else
        {
            break;
        }
    }
    array[hole] = tmp;
}
int main()
{
    int m;
    int num;
    vector<int> b;
    vector<int>::iterator itr;
    cout<<"Please enter the m numbers:"<<endl;
    freopen("111","r",stdin);
    cin>>m;
    for(int i=0;i<m;i++)
    {
        cin>>num;
        b.push_back(num);
    }
    BinaryHeap<int> test(b);
    cout<<"minimum value is:"<<test.findMin()<<endl;  //查找当前的最小值
    test.print();
    test.insert(51);
    test.insert(62);
    test.insert(66);
    test.insert(77);
    test.insert(44);    //插入后仍然满足完全二叉树
    cout<<"after insert,heap is:"<<endl;
    test.print();
    test.deleteMin();
    cout<<"after delete,heap is:"<<endl;//根结点先被删除,然后堆的最后一个元素替代根结点,然后开始执行下滤,即比空穴小的和空穴交换
    test.print();
    test.makeEmpty();  //清除所有元素
    if(test.isEmpty())
    {
        cout<<"is empty!"<<endl;
    }
    else
    {
        cout<<"no empty!"<<endl;
    }
    //test.deleteMin();   //此句因为目前数组中元素已空,所以会按已设定的形式报错。
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值