(一)定义:
堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树(逻辑层面上)的数组(物理层面上)对象,常用来在一组变化频繁(发生增删查改的频率较高)的数据中寻找最值.将根结点最大的堆叫做最大堆或大根堆,这样可以找到堆中的最大值(根节点的值);根结点最小的堆叫做最小堆或小根堆,这样可以找到堆中的最小值(根的最小值)。
(二)图文理解堆(以大顶堆为例)
大顶堆(大根堆):每个节点的值都大于或者等于他的左右孩子节点的值
小顶堆(小根堆):每个节点的值都小于或者等于他的左右孩子节点的值
如下图是以大顶堆入堆过程:数据5,12,4,1,45,65,6
逻辑层面:是一棵完全二叉树:
出堆过程:
每次出堆都是根节点,然后我们需要找到新的根节点:先下一层的节点比较谁(该节点)来当根节点(大顶堆就是谁大听谁的,小顶堆是我小我最大),然后还需要调整该节点下面的数据(每一个都需要调整,直至调整到最后一个元素),使之满足堆的定义(下一层谁当根节点,如果只有一个默认在右边)。
物理层面(实际上):数组存储
代码运行结果:
(三)堆的代码实现
①堆的结点类型&创建堆结点
#define MAX 12
//堆结点
typedef struct
{
int sizeHeap; //堆中的当前元素
int* heapData; //数组存储
}HEAP, * LPHEAP;
//创建堆
LPHEAP createHeap()
{
LPHEAP heap = (LPHEAP)malloc(sizeof(HEAP));
heap->sizeHeap = 0;
heap->heapData = (int*)malloc(sizeof(int) * MAX);
return heap;
}
②其他常见函数: 销毁堆;判断堆是否为空;打印堆;求堆当前元素与顺序表类似就不一一举例了
③入堆过程:
简单来说可以分成两步走: 直接在当前数组(容器)的最后位置(最后一个元素)插入+移动(改变堆)使之符合大顶堆(小顶堆)的要求
// 移动函数
void moveToCreectPos(LPHEAP heap, int curPos)
{
while (curPos > 1)
{
int Max = heap->heapData[curPos];
int parentIndex = curPos / 2; //跟当前节点的父节点比较
if (Max > heap->heapData[parentIndex])
{
heap->heapData[curPos] = heap->heapData[parentIndex];
heap->heapData[parentIndex] = Max;
curPos = parentIndex;
}
else
{
break; //当前元素小于父节点 ,满足要求,无需调整
}
}
}
// 插入节点函数
void insertHeap(LPHEAP heap, int data)
{
//是否是满的
if (heap->sizeHeap == MAX - 1)
{
return;
}
//直接在当前容器的最后位置(最后一个元素)插入
++heap->sizeHeap;
heap->heapData[heap->sizeHeap] = data;
// 2.交给其他函数去做调整使之符合大顶堆的定义
moveToCreectPos(heap, heap->sizeHeap);
}
④出堆过程:
//有序性的出堆
int popMax(LPHEAP heap)
{
int Max = heap->heapData[1]; //最大元素
//调整堆
int curPos = 1; //当前节点
int childPos = curPos * 2; //孩子节点:
while (childPos <= heap->sizeHeap)
{
int temp = heap->heapData[childPos];
//同一层上进行左右比较
if (childPos + 1 <= heap->sizeHeap && temp < heap->heapData[childPos + 1])
{
temp = heap->heapData[++childPos];
}
//向下去找
heap->heapData[curPos] = temp;
curPos = childPos;
childPos *= 2;
}
heap->heapData[curPos] = heap->heapData[heap->sizeHeap];
--heap->sizeHeap;
return Max;
}
完整代码: