C++ 堆结构(数组实现)

本文详细介绍了最大堆的数据结构,包括插入、删除及初始化等关键操作的实现原理与步骤,并通过示例代码展示了最大堆的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要说最大堆和最小堆,就得先知道最大树和最小树。

每个结点的值都大于(小于)或等于其子节点(如果有的话)值的树,就叫最大(最小)树。

最大堆(最小堆)是最大(最小)完全树。

由于堆是完全二叉树,所以可以用公式化描述,用一维数组来有效的描述堆结构。

利用二叉树的性质:

如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第[log2n]向下取整+1层,每层从左到右),则对任一结点i(1≤i≤n),有:

(1)如果i=1,则结点i无双亲,是二叉树的根;如果i>1,则其双亲是结点[i/2]向下取整。

(2)如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。

(3)如果2i+1>n,则结点i无右孩子;否则,其右孩子是结点2i+1。

这样就就可以将堆中结点移到它的父节点或者其中一个子节点处。

堆中进行的操作主要是:插入,删除,以及初始化。

下面只讨论最大堆。

在最大堆中进行插入操作,例如在下图所示的左边的最大堆中进行插入,插入后结构应该是和右边的图一样的。


插入时,现将新元素从新堆的父节点开始比较,也就是先从2所在结点开始比较,如果小于父节点的值,那么就直接作为孩子结点插入,如果大于,那么就要现将父节点下移,然后再和父节点的父节点比较,一直比较到根节点为止,所以插入过程,是从叶节点到根的一条路径。

最大堆的删除,从最大堆的删除时,该元素从根部移出。删除后,此时堆需要重新构造,以便于仍然为完全二叉树。例如下图:


先将根节点的值20取出后,为了结构仍然是二叉树,所以直接将最后一个结点,也就是保存2的结点去掉,然后将2的值保存下来,然后呢,就从根节点的两个孩子开始比较,看看2 根的左右孩子,这三个元素中,谁最大,那么根节点的值就是谁,然后就将2和空出来的结点的左右孩子(如果有的话)比较,确认子树的根节点的值,这样一步一步确认下去。

用数组初始化堆,数组元素显示按照下面的顺序,一个一个放在结点中


然后就从最后一个父节点开始,就是第五个结点,10所在的位置啦,从那里开始构造以该节点为根的最大堆。构造好后就移到下一个结点,15所在结点,以此类推,一直到根节点。

下面是代码:

文件"maxheap.h"

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. template <class T>  
  5. class MaxHeap  
  6. {  
  7. private:  
  8.     T *heap;  
  9.     int CurSize;  
  10.     int MaxSize;  
  11. public:  
  12.     MaxHeap(int maxsize=10)  
  13.     {  
  14.         MaxSize=maxsize;  
  15.         CurSize=0;  
  16.         heap=new T[MaxSize+1];  
  17.     }  
  18.   
  19.     ~MaxHeap()  
  20.     {  
  21.         delete[]heap;  
  22.     }  
  23.   
  24.     int Get_Size() const  
  25.     {  
  26.         return CurSize;  
  27.     }  
  28.   
  29.     T Get_Max()  
  30.     {  
  31.         if(CurSize==0)  
  32.         {  
  33.             cout<<"堆为空"<<endl;  
  34.             return -9999;  
  35.         }  
  36.         else  
  37.         {  
  38.             return heap[1];  
  39.         }  
  40.     }  
  41.   
  42.     MaxHeap<T> &Insert(const T& x)  
  43.     {  
  44.         if(CurSize==MaxSize)  
  45.         {  
  46.             cout<<"堆满,"<<x<<" 插入失败"<<endl;  
  47.             return *this;  
  48.         }  
  49.         //为x寻找应插入位置  
  50.         //i从新的叶子结点开始,沿着树向上  
  51.         int i=++CurSize;  
  52.         while(i!=1 && x>heap[i/2])  
  53.         {  
  54.             heap[i]=heap[i/2]; //将元素下移  
  55.             i/=2; //移向父节点  
  56.         }  
  57.           
  58.         heap[i]=x;  
  59.         cout<<x<<" 插入成功"<<endl;  
  60.         return *this;  
  61.     }  
  62.   
  63.     MaxHeap<T> &DeleteMax(T& x)  
  64.     {  
  65.         //将最大元素放入x,并从堆中删除它  
  66.         if(CurSize==0)  
  67.         {  
  68.             x=-9999;  
  69.             return *this;  
  70.         }  
  71.           
  72.         x=heap[1];  
  73.   
  74.         //重构堆  
  75.         heap[0]=heap[CurSize--]; //0号位置存放最后一个元素值,然后将该位置删除  
  76.   
  77.         //从根开始,为heap[0]寻找合适位置  
  78.         int i=1;  
  79.         int ci=2;  
  80.   
  81.         while(ci<=CurSize)  
  82.         {  
  83.             //ci是较大的孩子的位置  
  84.             if(ci<CurSize && heap[ci]<heap[ci+1])  
  85.                 ci++;  
  86.   
  87.             //判断是否可以放入heap[i]位置  
  88.             if(heap[0]>heap[ci])  
  89.                 break;  
  90.               
  91.             //不能  
  92.             heap[i]=heap[ci];  
  93.             i=ci; // 下移一层  
  94.             ci*=2;  
  95.         }  
  96.   
  97.         heap[i]=heap[0];  
  98.         return *this;  
  99.     }  
  100.   
  101.     void Init_heap(T a[],int size,int maxsize)  
  102.     {  
  103.         delete[]heap;  
  104.         heap=new T[maxsize+1];  
  105.         CurSize=size;  
  106.         MaxSize=maxsize;  
  107.   
  108.         for(int j=1;j<size+1;j++)  
  109.             heap[j]=a[j];  
  110.   
  111.         //产生一个最大堆  
  112.         for(int i=CurSize/2;i>=1;i--)  
  113.         {  
  114.             T y=heap[i]; //子树的根  
  115.   
  116.             //寻找放置y的位置  
  117.             int c=2*i;  
  118.             while(c<=CurSize)  
  119.             {  
  120.                 if(c<CurSize && heap[c]<heap[c+1])  
  121.                     c++;  
  122.   
  123.                 if(y>=heap[c])  
  124.                     break;  
  125.   
  126.                 heap[c/2]=heap[c];  
  127.                 c*=2;  
  128.             }  
  129.             heap[c/2]=y;  
  130.         }  
  131.     }  
  132. };  
测试文件"main.cpp"
  1. #include "maxheap.h"  
  2.   
  3. #include <iostream>  
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     MaxHeap<int> hp;  
  9.     int a[11]={-111,5,2,12,3,55,21,7,9,11,9};  
  10.       
  11.     cout<<"用数组a来初始化堆:"<<endl;  
  12.     for(int i=1;i<11;i++)  
  13.         cout<<a[i]<<"  ";  
  14.     cout<<endl;  
  15.   
  16.     hp.Init_heap(a,10,15);  
  17.   
  18.     int max;  
  19.     cout<<"目前堆中最大值为:"<<hp.Get_Max()<<endl;  
  20.   
  21.     hp.DeleteMax(max);  
  22.     cout<<"删除的堆中最大的元素为:"<<max<<endl;  
  23.     cout<<"删除后,现在堆中最大的元素为:"<<hp.Get_Max()<<endl;  
  24.   
  25.     cout<<"现在堆的大小为:"<<hp.Get_Size()<<endl;  
  26.   
  27.     cout<<"向堆中插入新元素"<<endl;  
  28.   
  29.     hp.Insert(22);  
  30.     hp.Insert(45);  
  31.     hp.Insert(214);  
  32.     hp.Insert(16);  
  33.     hp.Insert(21);  
  34.     hp.Insert(121);  
  35.     hp.Insert(111);  
  36.   
  37.     cout<<"插入后现在堆的大小为:"<<hp.Get_Size()<<endl;  
  38.   
  39.     cout<<"现在由大到小输出堆中元素"<<endl;  
  40.   
  41.     do  
  42.     {  
  43.         hp.DeleteMax(max);  
  44.         if(max== -9999)  
  45.             break;  
  46.         cout<<max<<"  ";  
  47.     }while(1);  
  48.   
  49.     cout<<endl;  
  50.   
  51.     return 0;  
  52. }  
测试结果:


在只是需要使用一个优先队列的时候,这种结构是十分有效率的,空间利用率也很高。但是并不适用于所有优先队列的使用,尤其是需要合并两个优先队列或多个长度不相等的队列的时候。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值