您的位置:首页 > 编程语言 > C语言/C++

大顶堆的C++模板实现及二叉堆的简介

2016-10-15 17:21 746 查看

何为二叉堆?

二叉堆是一种特殊的堆,二叉堆是完全二叉树或者近似完全二叉树。二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左右子树都是一个二叉堆

当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆(大顶堆)。

当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆(小顶堆)。

根据二叉树的性质,可得:

1. 如果根节点在数组的位置是1,第n个位置的子节点分别在2n和2n+1,第n个位置的双亲节点在[i/2]。因此,第1个位置的子节点在2和3。

2. 如果根节点在数组的位置是0,第n个位置的子节点分别在2n+1和2n+2,第n个位置的双亲节点在[(i-1)/2]。因此,第0个位置的子节点在1和2。

二叉堆一般用数组来表示,得益于数组的随机存储能力,我们能够很快确定堆中节点的父节点和子节点。



在本文中,我们主要介绍大顶堆的C++模板实现过程,以根节点在0位置存储为例。

二叉堆的具体实现

1. 二叉堆的抽象数据类型

/*大顶堆类定义*/
template <typename T>
class MaxHeap
{
public:
MaxHeap(int cap);
~MaxHeap();

bool insert(T val);     //往二叉堆中插入元素
bool remove(T val);     //从二叉堆中删除元素
void print();           //打印二叉堆
T    getTop();          //获取堆顶元素
bool creatMaxHeap(T a[],int size);//根据指定数组创建大顶堆

private:

int ArraySize;      //数组的大小
int size;           //堆的大小,数组中有效元素的个数
T*  maxheap;        //底层数组

//从index节点往根节点调整堆
void filterUp(int index);
//从begin节点向end节点调整堆
void filterDown(int begin,int end);

};


2. 构造函数与析构函数

/*构造函数*/
template <typename T>
MaxHeap<T>::MaxHeap(int cap)
{
ArraySize = cap;   //初始化数组的大小
size = 0;      
maxheap = new T[ArraySize]; //开辟空间
};
/*析构函数*/
template <typename T>
MaxHeap<T>::~MaxHeap()
{
delete []maxheap;      //释放空间  
}


3. 大顶堆的插入

在数组的最末尾插入节点,然后自上而下地调整子节点与父节点的位置:比较当前节点与父节点的大小,若不满足大顶堆的性质,则交换两节点。时间复杂度为O(log n)。




/*从下往上调整堆--插入元素时使用*/
template <typename T>
void MaxHeap<T>::filterUp(int index)
{
//记录下当前节点
T value = maxheap[index];
while(index>0)
{
//得到其双亲节点
int indexparent = (index - 1) / 2;

if(value < maxheap[indexparent])
break;
else  //交换两节点
{
//新节点被其父节点覆盖
maxheap[index] = maxheap[indexparent];
//记录下新节点的位置
index = indexparent;
}
}
//给新节点赋值
maxheap[index] = value;
};

/*插入元素*/
template<typename T>
bool MaxHeap<T>::insert(T val)
{
if(ArraySize == size)  //如果数组放不下
return false;
maxheap[size] = val;   //数组末尾放入新节点
filterUp(size);        //自下而上调整节点
size++;                //堆的元素数量加1
return true;
};


实际编码过程中,我们不进行节点的交换,我们直接使用父节点覆盖当前节点,然后记录新节点的位置,最后直接把新节点放入它最后的位置即可。

4. 大顶堆的删除

堆删除节点的过程:用数组最末尾节点覆盖被删结点,然后数组的有效元素(即堆中的元素)数量减1,再由该节点从上到下调整二叉堆。


/*从上到下调整堆--删除元素时使用*/
template <typename T>
void MaxHeap<T>::filterDown(int current,int end)
{
int child = current * 2 + 1;  //当前结点的左孩子

T value = maxheap[current];  //保存当前节点

while(child <= end)
{
//选出两个孩子中较大的孩子
if(child < end && maxheap[child] < maxheap[child + 1])
child++;
if(value > maxheap[child])//无需调整
break;
else
{
maxheap[current] = maxheap[child]; //孩子节点覆盖当前节点
current = child;                   //当前节点为较大的孩子节点
child = child * 2 + 1;             //child重新指向当前节点的左孩子节点
}
}
maxheap[current] = value;                  //找到合理地位置后 赋值
};
/*删除元素*/
template <typename T>
bool MaxHeap<T>::remove(T key)
{
if(size == 0)
return false;
int index;  //被删除元素的下标

for(index = 0; index < size; index++)
{

if(maxheap[index] == key)
break;  //找到被删元素的下标
}

if(index == size)
return false;  //数组中没有要删除的元素

//使用数组最后一个元素来代替该被删除元素
maxheap[index] = maxheap[size - 1];

//覆盖完成后 从上到下调整堆
filterDown(index,size--);

return true;
};


5. 其他操作

/*获取堆顶元素*/
template <typename T>
T MaxHeap<T>::getTop()
{
if(size != 0)
return maxheap[0];
};

/*打印大顶堆*/
template <typename T>
void MaxHeap<T>::print()
{
for(int i=0; i<size; i++)
cout << maxheap[i] << " ";
cout << endl;
};

/*根据指定数组创建大顶堆*/
template <typename T>
bool MaxHeap<T>::creatMaxHeap(T a[],int size)
{
if(ArraySize < size)
return false;
for(int i=0; i<size; i++)
insert(a[i]);

return true;
};


6. 测试代码

int main()
{
MaxHeap<int> heap1(11);

//逐个元素创建大顶堆
for(int i=0; i<10; i++)
heap1.insert(i);
cout << "堆顶元素为:" << heap1.getTop() << endl;
heap1.print();

/*从堆中删除元素5*/
heap1.remove(5);
heap1.print();

//根据指定数组创建大顶堆*/
int array[10] = { 1,2,3,4,5,6,7,8,9,10 };
MaxHeap<int> heap2(11);
heap2.creatMaxHeap(array,10);
cout << "堆顶元素为:" << heap2.getTop() << endl;
heap2.print();

return 0;
}


7. 输出结果

堆顶元素为:9
9 8 5 6 7 1 4 0 3 2
9 8 4 6 7 1 2 0 3
堆顶元素为:10
10 9 6 7 8 2 5 1 4 3


ok,all right.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: