背景
堆c语言代码实现,方便以后复习
1、堆的性质
1、堆的逻辑结构是一棵完全二叉树,堆的物理结构是数组(顺序表)
2、大堆:树中任意一个父节点的值>=其对应孩子的值
小堆:树中任意一个父节点的值<=其对应孩子的值
由于堆的逻辑结构是完全二叉树/满二叉树,在其底层存储时用一个数组顺序表)存储,存储数据时对应的父亲和孩子的索引值有这样的一个规律
leftchild = parent * 2 -1
rightchild = parent * 2 -2
parent = (child - 1)/2
2、堆中插入(push)数据分析
**堆中插入数据时需明确
1、在哪里插入? 2、插入时要满足堆的性质 (大堆/小堆)
在堆中插入数据时,先尾插数据,然后判断尾插的数据是否满足堆的性质,如果满足则直接尾插,不满足堆的性质,则需要向上调整。直到满足堆性质即可
*Notice:
*1、往堆中插入数据只会影响其祖先,不会影响其兄弟
2、向上调整的前提是前面的数据是堆
堆中插入数据流程如下图所示
这里还有一个问题就是向上调整的过程什么时候终止呢?
这里有两种方式
1、child所对应的节点为根节点时即 (child ==0)
2、parent<0时。
Notice:
这里其实对于parent<0是一个陷阱,parent不可能小于0,
最极端情况是等于0比如当child == 0时
parent = (child -1) /2 = 0,所有最极端情况也是等于0
这里一般终止条件采用child == 0
code
//向上调整
void AdjustUp(HPDataType* a, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]); //交换父亲和孩子索引所对应的值
child = parent; //更新索引值
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//堆中插入数据
void HeapPush(HP* php, int child)
{
assert(php != NULL);
if (php->size == php->capacity) //如果容量已满,扩容
{
//扩容
int newCapacity = ((php->capacity == 0) ? 4 : sizeof(php->a) * 2);
HPDataType* ptr = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);
if (ptr == NULL)
{
perror("realloc fail");
exit(-1);
}
php->a = ptr;
php->capacity = newCapacity;
}
php->a[php->size] = child; //插入数据
php->size++;
AdjustUp(php->a, php->size -1); //向上调整是对已有的数据通过其索引来找到对应的值进行调整,所有只需要传入php->a以及尾插数据的索引值
}
3、堆中删除(pop)数据分析
pop堆中的数据要明确
1、pop哪里的数据?
pop堆中数据时,pop的默认就是根节点(也就是数组下标为0时所对应的元索),而不是最后一个叶子节点。要是最后一个叶子节点也不需要你pop呀,直接–size不就好了嘛
2、如何pop这个数据?
pop根节点时有两种方式
(1)直接将根节点之后的元素前移覆盖根节点
**如果直接采用挪动覆盖第一个根节点位置的方法,则此时pop之后剩余的元素关系全乱了(兄弟变父子),剩余的值也不定是一个堆(特殊情况下可能是堆)
所以直接挪动覆盖第一个根节点位置的方法不可取。
(2)根节点和最后一个叶子节点值进行交换,然后在删除最后一个叶子节点,最后向下调整直到满足堆的性质为止(其实就是相当于删除了根节点)
code
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n) //由于这里的n为元素的个数所以child最大为n-1
{
if ((child +1 < n)&&(a[child] > a[child + 1])) //如果大于则左孩子变右孩子
{
++child;
}
if (a[parent] > a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//pop堆中数据
void HeapPop(HP* php)
{
assert(php != NULL);
Swap(&(php->a[0]), &(php->a[php->size - 1]));
--php->size;
AdjustDown(php->a, php->size, 0);
}
4、堆的实现code
heap.h
#pragma once
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
void HeapInit(HP* php);
void HeapDestory(HP* php);
void HeapPush(HP* php, int child);
void HeapPop(HP* php);
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent); //堆 堆中元素个数 根节点的索引值
void Swap(int* child, int* parent);
void HeapPrint(HP* php);
heap.c
#include"heap.h"
//初始化
void HeapInit(HP* php)
{
assert(php != NULL);
php->a = NULL;
php->capacity = 0;
php->size = 0;
}
//销毁
void HeapDestory(HP* php)
{
assert(php != NULL);
free(php->a);
php->capacity = 0;
php->size = 0;
}
void HeapPrint(HP* php)
{
assert(php != NULL);
for (int i = 0; i < php->size; ++i)
{
printf("%d ", php->a[i]);
}
printf("\n");
}
void Swap(int* child, int* parent)
{
int temp = *parent;
*parent = *child;
*child = temp;
}
//向上调整
void AdjustUp(HPDataType* a, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]); //交换父亲和孩子索引所对应的值
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n) //由于这里的n为元素的个数所以child最大为n-1
{
if ((child +1 < n)&&(a[child] > a[child + 1])) //如果大于则左孩子变右孩子
{
++child;
}
if (a[parent] > a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//堆中插入数据
void HeapPush(HP* php, int child)
{
assert(php != NULL);
if (php->size == php->capacity) //如果容量已满,扩容
{
//扩容
int newCapacity = ((php->capacity == 0) ? 4 : sizeof(php->a) * 2);
HPDataType* ptr = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);
if (ptr == NULL)
{
perror("realloc fail");
exit(-1);
}
php->a = ptr;
php->capacity = newCapacity;
}
php->a[php->size] = child; //插入数据
php->size++;
AdjustUp(php->a, php->size -1); //向上调整是对已有的数据通过其索引来找到对应的值进行调整,所有只需要传入php->a以及尾插数据的索引值
}
//pop堆中数据
void HeapPop(HP* php)
{
assert(php != NULL);
Swap(&(php->a[0]), &(php->a[php->size - 1]));
--php->size;
AdjustDown(php->a, php->size, 0);
}