数据结构 之 二叉堆

来源:互联网 发布:阿里云硬盘挂载 编辑:程序博客网 时间:2024/05/22 06:24


一、二叉堆简介: 
二叉堆故名思议是一种特殊的堆,二叉堆具有堆的性质(父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值),二叉堆又具有二叉树的性质(二叉堆是完全二叉树或者是近似完全二叉树)。当父节点的键值大于或等于(小于或等于)它的每一个子节点的键值时我们称它为最大堆(最小堆),或者是大根堆(小根堆)。
     二叉堆多数是以数组作为它们底层元素的存储。
假如根节点在数组中的索引是1,那么根结点的左右子节点在数组中的位置是 2,3;存储在第n个位置的父节点,它的左右子节点在数组中的存储位置为2n与2n+1。
假如根节点在数组中的索引是0,那么根结点的左右子节点在数组中的位置是 1,2。存储在第n个位置的父节点,它的左右子节点在数组中的存储位置为2n+1与2n+2。
例如:

 在这个图中,数组中的第一个元素(下标为0)就没有使用。
二、基本算法:
在二叉堆中,有两个基本的辅助算法:Sift-up(元素上移调整)  和  Sift-down(元素下移调整)。
下面分别给出其伪代码:




三、大根堆
下面直接贴出大根堆的实现代码:(在这个实现中,二叉堆中根结点在数组中的位置是从下标0开始的
//最大堆的下滑调整void siftdown(int *heap,int start,int m)  //m表示堆元素个数{                                         //注意数组下标是从0开始的    int i=start,j=2*i+1;    int temp=heap[i];    while(j<=m)    {        if(j<m&&heap[j]<heap[j+1]) j++; //如果右结点大于左节点        if(temp>=heap[j]) break;        else        {            heap[i]=heap[j];            i=j;            j=2*j+1;        }    }    heap[i]=temp;}//最大堆的上滑调整void siftup(int *heap,int start){    int j=start,i=(j-1)/2;    int 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;}//向最大堆插入一个元素xbool insert(int *heap,int &n,int x)   //将x插入到最大堆中{                                     //n表示堆元素个数  这里的n加一之后返回    heap[n]=x;    siftup(heap,n);   //从插入位置开始向上调整    n++;    return true;}//删除堆顶元素bool remove(int *heap,int &n,int &x)   //删除堆顶(最大元素)x返回堆顶元素{                                      //n表示堆元素个数  这里的n减一之后返回    x=heap[0];    heap[0]=heap[n-1]; //将堆中的最后一个元素转移到堆顶    n--;    siftdown(heap,0,n);  //从堆根位置开始从上向下调整    return true;}//创建一个最大堆void Creat_maxheap(int *arr,int n)  //建立最大堆                                   //n表示堆元素个数{    int currentsize=n;  //当前堆大小    int currentpos=(currentsize-2)/2; //开始调整位置    while(currentpos>=0)    {        siftdown(arr,currentpos,currentsize-1);        currentpos--;    }}//堆排序,利用大根堆从小到大排序数组void HeapSort(int *heap,int n){    for(int i=(n-2)/2;i>=0;i--)   //将数组先转换成为堆(即建堆),这里是大根堆        siftdown(heap,i,n-1);    for(int i=n-1;i>=1;i--)      //排序    {        swap(heap[0],heap[i]);        siftdown(heap,0,i-1);    }}
其中需要注意的是堆排序,在上面的堆排序中,需要排序的数组作为参数直接传入方法中,在堆排序方法中会执行建堆过程。
下面给出一个堆排序的例子:
int num[8]={53,17,78,9,45,65,87,23};    int n=8;    cout<<"排序前:";    for(int j=0;j<n;j++) cout<<num[j]<<" ";    cout<<endl;    HeapSort(num,n);    cout<<"排序后:";    for(int j=0;j<n;j++) cout<<num[j]<<" ";    cout<<endl;

运行结果:
从运行结果可以知道,利用最大堆进行排序的结果是非降序的。

四、小根堆
对照上文中的大根堆的实现代码,很快我们可以写成小根堆的实现代码如下:(注意:小根堆的根结点元素在数组中的位置也是从0开始的)
//最小堆的下滑调整void siftdown(int *heap,int start,int m)  //m表示堆元素个数{    int i=start,j=2*i+1;    int temp=heap[i];    while(j<=m)    {        if(j<m&&heap[j]>heap[j+1]) j++;        if(temp<heap[j]) break;        else        {            heap[i]=heap[j];            i=j;            j=2*j+1;        }    }    heap[i]=temp;}//最小堆的上滑调整void siftup(int *heap,int start){    int j=start,i=(j-1)/2;    int 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;}//建立最小堆void Creat_minheap(int *arr,int n)  //n表示堆元素个数{    int currentsize=n;  //当前堆大小    int currentpos=(currentsize-2)/2;    while(currentpos>=0)    {        siftdown(arr,currentpos,currentsize-1);        currentpos--;    }}//插入一个元素bool insert(int *heap,int &n,int x)   //将x插入到最小堆中{                                     //n表示堆元素个数  这里的n加一之后返回    heap[n]=x;    siftup(heap,n);    n++;    return true;}//删除堆顶元素bool remove(int *heap,int &n,int &x)   //删除堆顶(最小元素)x返回堆顶元素{                                      //n表示堆元素个数  这里的n减一之后返回    x=heap[0];    heap[0]=heap[n-1];    n--;    siftdown(heap,0,n);    return true;}//堆排序,利用小根堆从大到小排序数组void HeapSort(int *heap,int n){    for(int i=(n-2)/2;i>=0;i--)  //将数组(表)转换成为堆(建堆),这里是小根堆        siftdown(heap,i,n-1);    for(int i=n-1;i>=0;i--)      //排序    {        swap(heap[0],heap[i]);        siftdown(heap,0,i-1);    }}

同样我们还是注意到这里的堆排序:
同样执行上面的一个堆排序例子:
int num[8]={53,17,78,9,45,65,87,23};    int n=8;    cout<<"排序前:";    for(int j=0;j<n;j++) cout<<num[j]<<" ";    cout<<endl;    HeapSort(num,n);    cout<<"排序后:";    for(int j=0;j<n;j++) cout<<num[j]<<" ";    cout<<endl;

运行结果:

从运行结果可以知道,利用最小堆进行排序的结果是非升序的。

原创粉丝点击