c语言堆
一、定义:
“优先队列” (Priority Queue)是特殊的“队列”,从堆中取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。采用完全二叉树存储的优先队列 称为堆(Heap)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSQ3piUZ-1645324036393)(C:\Users\Π\AppData\Roaming\Typora\typora-user-images\image-20211130201925755.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jribiddp-1645324036394)(C:\Users\Π\AppData\Roaming\Typora\typora-user-images\image-20211130202336664.png)]
类型名称:最大堆(MaxHeap)
数据对象集:一个有N>0个元素的最大堆H是一棵完全二叉树,每个结点上的元素值不小于其子结点元素的值。
操作集:对于任意最多有MaxSize个元素的最大堆H MaxHeap,元素item ElementType,主要操作有:
MaxHeap Create( int MaxSize )//创建一个空的最大堆。
Boolean IsFull( MaxHeap H )//判断最大堆H是否已满。
Insert( MaxHeap H, ElementType item )//将元素item插入最大堆H。
Boolean IsEmpty( MaxHeap H )//判断最大堆H是否为空。
ElementType DeleteMax( MaxHeap H )//返回H中最大元素(高优先级)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-51jUPXaO-1645324036394)(C:\Users\Π\AppData\Roaming\Typora\typora-user-images\image-20211130202438933.png)]
二、最大堆的操作
最大堆的创建:
struct HeapStruct {
ElementType *Elements; /* 存储堆元素的数组 */
int Size; /* 堆的当前元素个数 */
int Capacity; /* 堆的最大容量 */
};
typedef struct HeapStruct *MaxHeap;
MaxHeap Create( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */
MaxHeap H =(MaxHeap)malloc( sizeof( struct HeapStruct ) );
H->Elements =(MaxHeap)malloc( (MaxSize+1) * sizeof(ElementType));
//从1开始,0作为哨兵
H->Size = 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData;
/* 定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作 */
return H;
}
//把MaxData换成小于堆中所有元素的MinData,同样适用于创建最小堆。
最大堆的插入:
void Insert( MaxHeap H, ElementType item )
{ /* 将元素item 插入最大堆H,其中H->Elements[0]已经定义为哨兵 */
int i;
if ( IsFull(H) ) {
printf("最大堆已满");
return;
}
i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
for ( ; H->Elements[i/2] < item; i/=2 )
H->Elements[i] = H->Elements[i/2]; /* 向下过滤结点 */
H->Elements[i] = item; /* 将item 插入 在数据大于所有节点数据时哨兵起效*/
}
//T(N)=O(logN)
最大堆的删除 删除最大值:
ElementType DeleteMax( MaxHeap H )
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
int Parent, Child;
ElementType MaxItem, temp;
if ( IsEmpty(H) ) {
printf("最大堆已为空");
return;
}
MaxItem = H->Elements[1]; /* 取出根结点最大值 */
/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
temp = H->Elements[H->Size--];
for( Parent=1; Parent*2<=H->Size; Parent=Child ) {
Child = Parent * 2;
if( (Child!= H->Size) &&
(H->Elements[Child] < H->Elements[Child+1]) )
Child++; /* Child指向左右子结点的较大者 */
if( temp >= H->Elements[Child] ) break;//退出的窗口
else /* 移动temp元素到下一层 */
H->Elements[Parent] = H->Elements[Child];
}
H->Elements[Parent] = temp;
return MaxItem;
最大堆的建立:“建立最大堆”是指如何将已经存在的N个元素按最大堆的要求存放在一个一维数组中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lboKjScr-1645324036395)(C:\Users\Π\AppData\Roaming\Typora\typora-user-images\image-20211130203408548.png)]
MaxHeap BuildMaxHeap( MaxHeap H )
{ /* 这里假设所有H->Size个元素已经存在H->Elements[]中 */
/* 本函数将H->Elements[]中的元素调整,通过从最小的每个子树开始往上,逐步满足要求,使满足最大堆的有序性 */
int Parent, Child, i;
ElementType temp;
for( i = H->Size/2; i>0; i-- ){ /*从最后一个结点的父结点开始 */
temp = H->Elements[i];
for( Parent=i; Parent*2<=H->Size; Parent=Child ) {
/* 向下过滤 */
Child = Parent * 2;
if( (Child!= H->Size) &&
(H->Elements[Child] < H->Elements[Child+1]) )
Child++; /* Child指向左右子结点的较大者 */
if( temp >= H->Elements[Child] ) break;
else /* 移动temp元素到下一层 */
H->Elements[Parent] = H->Elements[Child];
} /* 结束内部for循环对以H->Elements[i]为根的子树的调整 */
H->Elements[Parent] = temp;
}
return H;
}
c++模板
类模板
//堆结构
template <typename T>
class CMyHeap
{
//数据域
T* pBuff;
size_t len;
size_t MaxSize;
public:
CMyHeap();
~CMyHeap();
//拷贝构造
CMyHeap(CMyHeap const& heap);
void clear();
//添加删除初始化
void appEndNode(T const& srcData);
void deleteNode();
void initHeap(T arr[], size_t srcLen);
T * out(){ return pBuff; } //返回的是地址
};
/*
堆的初始化
1、加入数组的所有数据
2、依次调整,根据删除逻辑调整,从最后一个有子树的结点开始调整
*/
template<typename T>
void CMyHeap<T>::initHeap(T arr[], size_t srcLen)
{
//1、清空容器
clear();
//2、插入内容为空 退出
if (srcLen == 0)
return;
//3、数据入堆
len = MaxSize = srcLen;
pBuff = new T[MaxSize];
for (int i = 0; i < len; ++i)
pBuff[i] = arr[i];
//4、调整位置
for (int i = (int)((len - 2) >> 1); i >= 0; --i)//要调整的次数
{
//4.1、保存信息
int index = i;
T tempData = pBuff[index];//保存的值
//4.2、正式调整
while (true)
{
//4.1、获取左右子树的下标
size_t left = 2 * index + 1;
size_t right = 2 * index + 2;
//4.2、判断有无子树
if (left > (len - 1))//比较到底了
break;
//存在子树
//4.3、设置一个标记,表示左右谁更大
bool isLeft = true;
if (right < len)//右子树存在
{
if (pBuff[left] < pBuff[right])//右子树更大
isLeft = false;//修改标记
}
//4.4、根节点与较大者比较
if (isLeft)
{
//和左子树比较
if (tempData < pBuff[left])
{
//左子树上位
pBuff[index] = pBuff[left];
index = left;//下标更新
}
else
break;//找到了位置 退出
}
else
{
//和右子树比较 false
if (tempData < pBuff[right])
{
//右子树上位
pBuff[index] = pBuff[right];
index = right;//下标更新
}
else
break;//找到了位置 退出
}
}
//5、循环结束 到叶节点了 到对应位置
pBuff[index] = tempData;
}
}
/*
堆的删除逻辑
1、每次删除都删根节点
2、把最后一个结点覆盖到根节点,元素减一
3、调整位置以满足堆特性
*/
template<typename T>
void CMyHeap<T>::deleteNode()
{
//1、空堆
if (len == 0)
return;
//2、元素大于1 最后一个结点覆盖到根节点
if (len > 1)
pBuff[0] = pBuff[len - 1];
len--;
//3、保存信息 下标和值
int index = 0;
T tempData = pBuff[index];
//4、开始调整
while (true)
{
//4.1、获取左右子树的下标
size_t left = 2 * index + 1;
size_t right = 2 * index + 2;
//4.2、判断有无子树
if (left > (len - 1))//比较到底了
break;
//否则存在子树
//4.3、设置一个标记,表示左右谁更大
bool isLeft = true;
if (right < len)//右子树存在
{
if (pBuff[left] < pBuff[right])//右子树更大
isLeft = false;//修改标记
}
//4.4、根节点与较大者比较
if (isLeft)
{
//和左子树比较
if (tempData < pBuff[left])
{
//左子树上位
pBuff[index] = pBuff[left];
index = left;//下标更新
}
else
break;//找到了位置 退出
}
else
{
//和右子树比较 false
if (tempData < pBuff[right])
{
//右子树上位
pBuff[index] = pBuff[right];
index = right;//下标更新
}
else
break;//找到了位置 退出
}
}
//5、循环结束 到叶节点了 到对应位置
pBuff[index] = tempData;
}
template<typename T>
void CMyHeap<T>::appEndNode(T const& srcData)
{
//1、扩容
if (len >= MaxSize)
{
//1.1、半倍扩容
MaxSize += ((MaxSize >> 1) > 1 ? (MaxSize >> 1) : 1);
T * pTemp = new T[MaxSize];
//1.2、复制转移
for (size_t i = 0; i < len; ++i)
{
pTemp[i] = pBuff[i];
}
//1.3、过河拆桥
if (pBuff)
delete[] pBuff;
pBuff = pTemp;
}
//扩容完成,插入数据
pBuff[len++] = srcData;
//2、记录信息 - 下标与数值
int tempIndex = len - 1;
T tempData = pBuff[tempIndex];
//3、调整位置
while (tempIndex)//=0 找到根节点
{
int parentIndex = (tempIndex - 1) >> 1;//右移除2到父节点
if (pBuff[parentIndex] < tempData)//父节点比子结点小 数据下移
pBuff[tempIndex] = pBuff[parentIndex];
else
break;
//循环的步长
tempIndex = parentIndex;
}
//出循环了 要不找到了合适的位置 要不就找到根节点了,此时根节点作为哨兵
//4、不管是什么原因 都需要插入的最后一步 放入数据
pBuff[tempIndex] = tempData;
}
template<typename T>
void CMyHeap<T>::clear()
{
if (pBuff)
delete[] pBuff;
pBuff = nullptr;
len = MaxSize = 0;
}
template<typename T>
CMyHeap<T>::CMyHeap(CMyHeap const& heap)
{
this->pBuff = new T[heap.MaxSize];
MaxSize = heap.MaxSize;
len = heap.len;
for (int i = 0; i < len; ++i)
{
pBuff[i] = heap.pBuff[i];
}
}
template<typename T>
CMyHeap<T>::~CMyHeap()
{
clear();
}
template<typename T>
CMyHeap<T>::CMyHeap()
{
pBuff = nullptr;
len = MaxSize = 0;
}
主函数调试
#include <iostream>
#include <string>
#include "CMyHeap.hpp"
using namespace std;
int main()
{
CMyHeap<int> mh;
mh.appEndNode(1);
mh.appEndNode(2);
mh.appEndNode(3);
mh.appEndNode(1);
mh.deleteNode();
int arr[]{1, 2, 3, 4, 5};
mh.initHeap(arr, 5);
CMyHeap<int> mh2(mh);
cout << mh.out() << endl;
//system("pause");
return 0;
}
主函数调试
#include <iostream>
#include <string>
#include "CMyHeap.hpp"
using namespace std;
int main()
{
CMyHeap<int> mh;
mh.appEndNode(1);
mh.appEndNode(2);
mh.appEndNode(3);
mh.appEndNode(1);
mh.deleteNode();
int arr[]{1, 2, 3, 4, 5};
mh.initHeap(arr, 5);
CMyHeap<int> mh2(mh);
cout << mh.out() << endl;
//system("pause");
return 0;
}