选用 向上调整法 与 向下调整法的时机:
AdjustDown() 与 AdjustUp() 的选取:
注意 AdjustDown() 与 AdjustUp() 的参数不同
在不进行插入元素或者删除元素的前提下:
如果要建立小根堆,则需要使用 AdjustUp(),
如果要建立大根堆,则需要使用 AdjustDown().
当要在堆中插入元素时:
无论是建立小根堆还是大根堆,则需要使用 AdjustUp(),
当要在堆中删除元素时:
无论是建立小根堆还是大根堆,则需要使用 AdjustDown()
堆操作的代码实现:
#include <iostream>
using namespace std;
int main()
{
/*实现堆的内存结构是数组;
堆分为大根堆和小根堆;
堆是一个完全二叉树。*/
typedef int HeapDataType;
typedef struct Heap
{
HeapDataType *arr;
int size;
int capacity;
}Heap;
//向下调整算法:
void AdjustDown(int *arr, int n, int parent)//要传递的是三个参数
{
int child = parent * 2 + 1;//默认先求出左孩子结点
while (child < n)
{
/*child + 1 < n 的原因是预防在访问数组时发生越界,
因为默认 child 是左孩子结点,
而堆又是完全二叉树,
所以当访问到左孩子结点时,一定不会发生数组越界,
因为最后右树的右孩子结点可有可无,
所以要防止访问到右孩子越界。*/
if ( (child + 1 < n) && arr[child] < arr[child + 1])/*左孩子的结点比右结点小,
而父亲结点是要与俩个孩子结点中最大的那一个结点进行交换*/
{
child++;
}
if (arr[parent] < arr[child])
{
swap(&arr[parent], &arr[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//初始化堆(建堆):
/*注意:
这里的初始化不像顺序表那样只是简单的赋值,
根据堆的特性,
不光要赋值,还得对数组里面的元素进行排序(使用向下调整算法进行大根堆中元素的排序)。
void HeapInit(Heap *ph, int n)/*第一个参数是堆的地址,
第二个参数是动态数组,
第三个参数是堆中元素的个数*/
{
assert(ph);
HeapDataType *tmp = (HeapDatatType *)malloc(sizeof(HeapDataType) * n);
if (ph->arr == NULL)
{
printf("malloc fail\n");
exit(-1);
}
ph->arr = tmp;
php->size = n;
php->capacity = n;
//建堆:
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(php->a, php->size, i);
}
}
/*-----------------------------------------------------------------------------------*/
void HeapInit(Heap *hp)
{
assert(hp);
hp->arr = NULL;
hp->size = hp->capacity = 0;
}
//堆结构的销毁:
/*注意:
它没有对堆的整体地址 free 掉*/
void HeapDestroy(Heap *hp)
{
assert(hp);
free(hp->arr);
hp->arr = NULL;
hp->size = hp->capzcity = 0;
}
//堆的插入:
void HeapPush(Heap *ph, HeapDataType x)
{
if (ph->size == ph->capacity)
{
int newcapacity == 0 ? 4 : ph->capacity * 2;/*对于 hp->capacity 的更新,
必须创建一个新的变量,
因为有可能 capacity 的初始值为 0,
如果直接与 sizeof(HeapDataType) 相乘,
则会出错。*/
HeapDataType *tmp = (HeapDataType *)realloc(ph->arr, sizeof(HeapDataType)*newcapacity);
if (tmp == NULL)
{
cout << "realloc is fail" << endl;
exit(-1);
}
ph->arr = tmp;
ph->capacity = newcapacity;
}
ph->arr[size] = x;
ph->size++;
//插入后就要对整个数组进行重新排序,使之变为大根堆或小根堆,
/*这里以小根堆为例,使用向上调整算法*/
AdjustUp(hp->arr,hp->size-1);
}
//向上调整算法:
void AdjustUp(HeapDataType *arr, int child)//无需传入双亲结点的下标,只需将插入元素的下标传入即可
{
assert(arr);
int parent = (child - 1) / 2;
while (child > 0)
{
if (arr[child] < arr[parent])
{
swap(&arr[child], &arr[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//堆元素的删除(删除数组的首元素):
/*如果要删除堆中的元素时,一般从堆的末尾开始删除,
所以如果想要删除数组中的第一个元素,
则需要将其与末尾最后一个元素互换,
然后进行 size--,
最后对堆重新进行排序使之变为大根堆或小根堆*/
void HeapPop(Heap *hp)//注意其参数是堆的地址
{
assert(hp);
swap(&hp->arr[0], &hp->arr[hp->size - 1]);
hp->size--;//只需减小size就算是对于元素的删除
AdjustDown(hp, hp->size, 0);
}
//AdjustDown() 与 AdjustUp() 的选取:
/*注意 AdjustDown() 与 AdjustUp() 的参数不同*/
/*在不进行插入元素或者删除元素的前提下:
如果要建立小根堆,则需要使用 AdjustUp(),
如果要建立大根堆,则需要使用 AdjustDown().
当要在堆中插入元素时:
无论是建立小根堆还是大根堆,则需要使用 AdjustUp(),
当要在堆中删除元素时:
无论是建立小根堆还是大根堆,则需要使用 AdjustDown()*/
}