堆是一顆完全二叉樹,所有元素是按照完全二叉樹的順序存儲方式放在一個數組,堆中每個結點都比其孩子結點小(大),叫小(大)堆
所以,堆的底層是按數組方式來存儲,數組元素可能有序,可能無序,但堆中元素是有順序的,要麼是小堆,要麼是大堆。
下圖是數組對應的完全二叉樹,但還不是堆,所以在創建堆時,需要對數組進行向下調整,建成小堆或大堆
- 建堆(這裏我建的小堆,你也可以建大堆,後面會介紹如何一起來建大小堆)
如果從第一個節點調整,不能保證向下調整後,堆頂元素最小,因此不能滿足堆的特性,只能看出,雙親結點小於孩子節點,所以在調整時,需要從倒數第一個非葉子節點(size-1-1)/2開始,依次往上
void HeapAdjustDown(Heap* hp,int parent)
{
int child = parent * 2 + 1;
while (child < hp->size)//保證左孩子存在
{
if (hp->array[child] > hp->array[child + 1] && child + 1< hp->size)//保證右孩子存在,要不然咋比
child = child + 1;
if (hp->array[parent] > hp->array[child])
{
Swap(&hp->array[child], &hp->array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void HeapCreate(Heap* hp, DataType arr[], int size)//創建
{
assert(hp);
hp->array = (DataType*)malloc(sizeof(DataType)*size);
if (hp->array==NULL)//
{
assert(0);//看爲什麼失敗
return;
}
hp->capacity = size;
memcpy(hp->array, arr, sizeof(DataType)*size);
hp->size = size;
//對堆中元素進行向下調整,重複調幾次
//調上面的左右子樹之前,要從倒數第一個非葉子結點開始
for (int root = (size - 2) >> 1; root >= 0;root--)
HeapAdjustDown(hp, root);
- 堆的插入與刪除
堆的插入是在數組末插入元素,所以插入後,需要向上調整,把該元素放到合適的位置
刪除是刪數組第一個元素,也就是堆頂,需要1.堆頂與堆尾元素交換,2.向下調整元素至合適位置
void HeapPush(Heap* hp, DataType data)//插入
{
assert(hp);
CheckCapacity(hp);//檢測還有無空間
hp->array[hp->size] = data;
hp->size++;
HeapAdjustUp(hp,hp->size-1);
}
void HeapPop(Heap* hp)//堆的刪除
{
assert(hp);
if (HeapEmpty(hp)) return;
Swap(&hp->array[0], &hp->array[hp->size - 1]);
hp->size--;
HeapAdjustDown(hp, 0);
}
Heap.h代碼
#include "Heap.h"
void Swap(int* left, int* right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}
void CheckCapacity(Heap* hp)
{
assert(hp);
int newCapacity = hp->capacity * 2;
if (hp->size >= hp->capacity)
{
//需要擴容
DataType* temp = (DataType*)malloc(sizeof(DataType)*newCapacity);
if (temp == NULL)
{
assert(0);
return;
}
//memcpy(temp, hp->array, sizeof(DataType)*hp->size);
for (int i = 0; i < hp->size; i++)
temp[i] = hp->array[i];
free(hp->array);
hp->array = temp;
hp->capacity = newCapacity;
}
}
void HeapAdjustUp(Heap* hp, int child)
{
int parent = (child - 1) >> 1;
//不需要找孩子中最小的與雙親比較
//插入之前已經滿足堆的特性了
while (child)
{
if (hp->array[child] < hp->array[parent])
{
Swap(&hp->array[child], &hp->array[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
return;
}
}
void HeapAdjustDown(Heap* hp,int parent)
{
int child = parent * 2 + 1;
while (child < hp->size)//保證左孩子存在
{
if (hp->array[child] > hp->array[child + 1] && child + 1< hp->size)//保證右孩子存在,要不然咋比
child = child + 1;
if (hp->array[parent] > hp->array[child])
{
Swap(&hp->array[child], &hp->array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void HeapCreate(Heap* hp, DataType arr[], int size)//創建
{
assert(hp);
hp->array = (DataType*)malloc(sizeof(DataType)*size);
if (hp->array==NULL)//
{
assert(0);//看爲什麼失敗
return;
}
hp->capacity = size;
memcpy(hp->array, arr, sizeof(DataType)*size);
hp->size = size;
//對堆中元素進行向下調整,重複調幾次
//調上面的左右子樹之前,要從倒數第一個非葉子結點開始
for (int root = (size - 2) >> 1; root >= 0;root--)
HeapAdjustDown(hp, root);
}
void HeapDestory(Heap* hp)//銷燬
{
assert(hp);
if (hp->array)
{
free(hp->array);
hp->array =NULL;
hp->capacity = 0;
hp->size = 0;
}
}
void HeapPush(Heap* hp, DataType data)//插入
{
assert(hp);
CheckCapacity(hp);
hp->array[hp->size] = data;
hp->size++;
HeapAdjustUp(hp,hp->size-1);
}
void HeapPop(Heap* hp)//堆的刪除
{
assert(hp);
if (HeapEmpty(hp)) return;
Swap(&hp->array[0], &hp->array[hp->size - 1]);
hp->size--;
HeapAdjustDown(hp, 0);
}
DataType HeapTop(Heap* hp)//取堆頂元素
{
assert(hp&&!HeapEmpty(hp));
return hp->array[0];
}
int HeapSize(Heap* hp)//堆的數據個數
{
assert(hp);
return hp->size;
}
int HeapEmpty(Heap* hp)//堆判空
{
assert(hp);
return hp->size == 0;
}