码迷,mamicode.com
首页 > 其他好文 > 详细

[数据结构]最小堆的类模板实现

时间:2015-07-30 19:34:42      阅读:414      评论:0      收藏:0      [点我收藏+]

标签:   最小堆   数据结构   二叉树   c++   

堆数据结构是一种数组对象,它可以被视为一科完全二叉树结构

它的特点是父节点的值大于(小于)两个子节点的值(分别称为最大堆和最小堆)。它常用于管理算法执行过程中的信息,应用场景包括堆排序优先队列等。


1、根结点若有子树,则子树一定也是堆。

2
、根结点一定大于(或小于)子结点。


因为要求堆必须是完全二叉树,所以可以用线性的数据结构,比如数组,来实现堆。

利用数组实现,则对于长为N的堆中的元素从0N-1排列,有:


i
的父结点:Parent(i)=(i+1)/2-1


i
的左叶子:Left(i)=(i+1)*2-1


i
的右叶子:Right(i)=(i+1)*2

堆(Heap),其实也没什么大不了,简单地说就是一种有序队列而已,普通的队列是先入先出,而二叉堆是:最小先出。
这不是很简单么?如果这个队列是用数组实现的话那用打擂台的方式从头到尾找一遍,把最小的拿出来不就行了?行啊,可是出队的操作是很频繁的,而每次都得打一遍擂台,那就低效了,打擂台的时间复杂度为Ο(n),那如何不用从头到尾fetch一遍就出队呢?二叉堆能比较好地解决这个问题,不过之前先介绍一些概念。


完全树(Complete Tree):从下图中看出,在第n层深度被填满之前,不会开始填第n+1层深度,还有一定是从左往右填满。

技术分享

这样有什么好处呢?好处就是能方便地把指针省略掉,用一个简单的数组来表示一棵树,如图:

技术分享
那么下面介绍二叉堆:二叉堆是一种完全二叉树,其任意子树的左右节点(如果有的话)的键值一定比根节点大,上图其实就是一个二叉堆。
你一定发觉了,最小的一个元素就是数组第一个元素,那么二叉堆这种有序队列如何入队呢?看图:

技术分享
假设要在这个二叉堆里入队一个单元,键值为2,那只需在数组末尾加入这个元素,然后尽可能把这个元素往上挪,直到挪不动,经过了这种复杂度为Ο(logn)的操作,二叉堆还是二叉堆。

那如何出队呢?也不难,看图:

技术分享

将最小的节点删除后,把最后的一个节点放到第一个节点的位置,然后在调整。


最小堆的类模板实现:类接口部分

/////////////////////////
#include <iostream>
using namespace std;


#define DefaultSize 10

template<class T>
class MinHeap       //最小堆的类模板实现
{
public:
    MinHeap(int sz=DefaultSize);
    MinHeap(T arr[], int n);
    ~MinHeap(){delete []heap;}

    bool isEmpty()const{return currentSize==0;}
    bool isFull()const{return currentSize==maxHeapSize;}
    void makeempty(){currentSize=0;}

    bool insert(const T& x); //在数组尾部插入,并调整堆
    bool removeMin(T& x);    //删除堆顶上的最小元素,最后一个元素补到堆顶,然后调整


private:
    T * heap;       //采用数组作为其存储方式。
    int currentSize;
    int maxHeapSize;

    void siftDown(int start, int m);//从start到m下滑调整为最小堆
    void siftUp(int start);         //从start到0上滑调整为最小堆
};

具体接口函数实现部分:

template<class T>
MinHeap<T>::MinHeap(int sz=DefaultSize)
{
    maxHeapSize=(sz<DefaultSize)?DefaultSize:sz;
    heap=new T[maxHeapSize];
    if(heap==NULL){cerr<<"err\n";exit(-1);}
    currentSize=0;
}

template<class T>
MinHeap<T>::MinHeap(T arr[], int n)
{
    maxHeapSize=(n<DefaultSize)?DefaultSize:n;
    heap=new T[maxHeapSize];
    if(heap==NULL){cerr<<"err\n";exit(-1);}
    currentSize=n;

    int i=0;
    while(i<n){                 //copy
        heap[i]=arr[i];
        ++i;
    }

    int currentPos=(currentSize-2)/2; //找到最初调整位置,最后节点的父节点位置,也就是最后的分支节点
    while(currentPos>=0){             //自底向上逐步扩大形成堆
        siftDown(currentPos,currentSize-1);
        --currentPos;
    }
}

template<class T>
void MinHeap<T>::siftDown(int start, int m)//堆的下滑调整算法,从节点start到m为止,从上到下比较,如果子女小于父节点,
{                                          //关键码上浮 ,据需向下层比教,将局部子树调整为最小堆
    int i=start;int j=2*i+1;    //i当前子树的根节点,j左子女
    T temp=heap[i];             //保存根节点的值
    while(j<=m){                //从上向下调整,检查是否到最后位置
        if(j<m && heap[j+1]<heap[j])    //j指向两个子女中最小的
            ++j;
        if(temp<=heap[j]) break;        //小则不做调整
        else{
            heap[i]=heap[j];i=j;j=2*i+1;//小者上移,i,j下降
        }
    }
    heap[i]=temp;
}

template<class T>
void  MinHeap<T>::siftUp(int start)//新节点插入到最小堆的后面,故需从下到上,与父节点比较,调整
{                                  //从start开始到0为止,从下向上
    int j=start, i=(j-1)/2;        //j表示子节点,i表示j的父节点
    int temp=heap[j];
    while(j>0){
        if(heap[i]<=temp)break;
        else{
            heap[j]=heap[i];j=i;i=(j-1)/2;  
        }
        heap[j]=temp;
    }
}

template<class T>
bool MinHeap<T>::insert(const T& x)
{
    if(currentSize==maxHeapSize){
        cerr<<"full\n";return false;
    }
    heap[currentSize]=x;
    siftUp(currentSize);
    ++currentSize;
    return true;
}

template<class T>
bool MinHeap<T>::removeMin(T& x)
{
    if(!currentSize){
        cerr<<"empty\n";return false;
    }
    x=heap[0];
    heap[0]=heap[currentSize-1];
    --currentSize;
    siftDown(0,currentSize-1);
    return true;
}

验证程序:

为了简单,验证时把类中private屏蔽掉,以便外面访问;

int main(int argc, char* argv[])
{
    int arr[6]={5,4,3,2,1,0};
    int i=0;
    while(i<sizeof(arr)/sizeof(arr[0])){
        cout<<arr[i]<<" ";
        ++i;
    }
    cout<<endl;

    MinHeap<int> h(arr,sizeof(arr)/sizeof(arr[0]));

    i=0;
    while(i<6){
        cout<<h.heap[i]<<" ";
        ++i;
    }
    cout<<endl;

    int x=10;
    h.insert(x);
    i=0;
    while(i<7){
        cout<<h.heap[i]<<" ";
        ++i;
    }
    cout<<endl;

    h.removeMin(x);
    i=0;
    while(i<6){
        cout<<h.heap[i]<<" ";
        ++i;
    }
    cout<<endl;
    system("pause");
    return 0;

}

验证结果:

5 4 3 2 1 0
0 1 3 2 4 5
0 1 3 2 4 5 10
1 2 3 10 4 5
请按任意键继续. . .










版权声明:本文为【借你一秒】原创文章,转载请标明出处。

[数据结构]最小堆的类模板实现

标签:   最小堆   数据结构   二叉树   c++   

原文地址:http://blog.csdn.net/u013467442/article/details/47153781

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!