[普通]《算法之美》---二叉堆及其实现

作者(passion) 阅读(1087次) 评论(0) 分类( 面试题)

二叉堆是一棵满足下列性质的完全二叉树:

1)如果某节点有孩子,则根节点的值都小于孩子节点的值。我们称之为小根堆;

2)如果某节点有孩子,则根节点的值都大于孩子节点的值。我们称之为大根堆。

这样,如果我们用一维数组a存储来存储该堆,那么具有n个节点的堆可看成是一棵按层次排列,同一层按自左向右排列的完全二叉树。显然,小根堆的根节点是最小值,大根堆的根节点是最大值,计算过程分下面两步进行:

(1)建堆,把数组a中各个元素转变成堆;

(2)对数组a进行排序:

         通过相继地输出根节点,产生递增(递减)序列;

         保持余下节点的堆性质。

 

在调整中保持堆性质:

以小根堆为例,假设节点a[s]的左右子树都是小根堆,而添加a[s]后不满足小根堆性质,我们的方法是从根节点出发顺着左右子树的一边逐步进行调整,调整的过程实质上是让a[s]在小根堆中下降,最终满足以s为根的子树成为小根堆。过程如下:

1)找出s节点左右子节点中权值较小者,记其位置为j;

2)比较a[j]和a[s]:若a[s]的权值小于等于a[j],则调整完毕;否则a[s]<---a[j],s<---j,继续从s出发向下调整。

调整堆的伪代码如下所示:

//从a[s]出发向下调整建堆:若a[s]的左右孩子均大于a[s],则调整完毕;

//否则,找出其中较小者,其序号记为j;a[s]和a[j]的值交换,继续由a[j]往下调整

procedure heap(s, n : longint);

Var

         i, j, k : longint;

{

         j <-- 1; k <-- a[s];   //暂存a[s],a[s]左右孩子的较小者序号初始化为1

         while j <> maxint do

         {

                   i <-- a[s]; j <-- maxint; //找出a[s]的左右孩子的较小者,其序号为j

                   if s*2<=n       //左孩子

                            then if a[s*2]<i then {i<--a[s*2]; j<--s*2;}        //顺着左孩子方向找

                   if s*2+1<=n //右孩子

                            then if a[s*2+1]<i then{i<--a[s*2+1]; j<--(s*2+1); }         //顺着右孩子方向找

                   if j<>maxint then {a[s]<--i; s<--j; } //a[s]的值调整为其左右孩子较小者,继续从a[j]往下调整

         }

         a[s]<--k; //最后位置的值调整为原子根值

}

 

下面是最小堆的一个类模板及其测试代码:

========================asce.h===========================

#include <iostream>

 

template<typename T> class MinPQ //抽象类

{

public:

    virtual int Insert(const T&)=0; //纯虚函数

    virtual int RemoveMin(T&)=0;                     

};

 

//最小堆的定义

template<typename T>

class MinHeap : public MinPQ<T>

{

public:

    MinHeap(int maxSize);

    MinHeap(T arr[], int n);

    ~MinHeap() {delete[] heap;}

   

    int Insert(const T &x);    //入队

    int RemoveMin(T &x);   //出队

    bool IsEmpty() const

    {

        return currentSize == 0;    

    }

    bool IsFull() const

    {

        return currentSize == MaxHeapSize;    

    }

 

private:

    enum{DefaultSize = 10};

    T *heap;         //存放堆的数组

    int currentSize; //当前元素个数,堆尾

    int MaxHeapSize;

    void FilterDown(int start ,int endOfHeap);

    void FilterUp(int start);

};

 

//构造函数,根据给定大小maxSize,建立堆对象

template<typename T>

MinHeap<T>::MinHeap(int maxSize)

{

    //确定堆大小

    MaxHeapSize = DefaultSize<maxSize ? maxSize : DefaultSize;

    heap = new T[MaxHeapSize];        //创建堆数组空间

    currentSize = 0;

}

 

//构造函数,根据数组建立堆对象

template<typename T>

MinHeap<T>::MinHeap(T arr[], int n)

{

    //确定堆大小

    MaxHeapSize = DefaultSize<n ? n : DefaultSize;

    heap = new T[MaxHeapSize];

    heap = arr;  //数组传送

    currentSize = n; //当前堆大小

    int currentPos = (currentSize-1)/2; //最后的非叶子节点 

    while(currentPos >= 0)

    {

        //从上到下逐步扩大,形成堆

        FilterDown(currentPos, currentSize-1);

        //从currentPos开始到currentSize为止,调整

        currentPos--;                

    }

}

 

//最小堆的向下调整算法

template<typename T>

void MinHeap<T>::FilterDown(int start, int endOfHeap)

{

    int i = start;

    int j = 2*i + 1; //j是的左孩子

    T temp = heap[i];

    while(j <= endOfHeap)

    {

        if(j < endOfHeap && heap[j] > heap[j+1])

            j++; //左右两孩子中选择较小者,即heap[j+1]

        if(temp <= heap[j])

            break//调整结束

        else

        {

            heap[i] = heap[j];

            i = j;

            j = 2*j + 1;   

        }       

    }   

    heap[i] = temp;

}

 

//堆的插入操作

template<typename T>

int MinHeap<T>::Insert(const T &x)

{

    //在堆中插入新元素x

    if(currentSize == MaxHeapSize) //堆满

    {

        std::cout<<"堆已满"<<std::endl;

        return 0;              

    }   

    heap[currentSize] = x; //插在表尾

    FilterUp(currentSize); //向上调整堆

    currentSize++;       //堆元素增1

    return 1;

}

 

//最小堆的向上调整算法(堆已建好,用于插入)

template<typename T>

void MinHeap<T>::FilterUp(int start)

{

    //从start开始向上直到0,调整堆

    int j = start;

    int i = (j-1)/2; //i是j的双亲

    T temp = heap[j];

    while(j > 0)

    {

        if(heap[i] <= temp)

            break;

        else

        {

            heap[j] = heap[i];

            j = i;

            i = (i-1)/2;   

        }       

    }    

    heap[j] = temp;

}

 

 

//最小堆的删除算法(出堆),输入参数x用于接收出堆的元素

//不断调用RemoveMin函数输出x就相当于升序排序了

template<typename T>

int MinHeap<T>::RemoveMin(T &x)

{

    if(currentSize <= 0)

    {

        std::cout<<"堆已空"<<std::endl;

        return 0;              

    }

    x = heap[0]; //最小元素heap[0]出队列

    heap[0] = heap[currentSize-1]; //用最后元素填补

    currentSize--;

    //从0号位置开始自上向下调整为最小堆

    FilterDown(0, currentSize-1);

   

    return 1;

}

 

=====================测试代码TestCase.cpp======================

#include "asce.h"

 

int main()

{

    int arr[] = {1, 4, 34, 6, 43, 10,9};

    MinHeap<int> ace(arr, 8);

    int show = 0;

    for(int i=0; i<7; i++)

    {

        ace.RemoveMin(show);

        std::cout<<show<<std::endl;               

    }

    system("pause");

    return 0;

}

 


« 上一篇:wifi共享上网(至尊版wifi)
« 下一篇:drcom至尊版使用openwrt路由器拨号
在这里写下您精彩的评论
  • 微信

  • QQ

  • 支付宝

返回首页
返回首页 img
返回顶部~
返回顶部 img