堆的实现
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的 子树开始调整,一直调整到根节点的树,就可以调整成堆。
堆分为大堆和小堆
大堆:每个父亲节点都比孩子节点大;
小堆:每个父亲节点都比孩子节点小;
向下调整
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆(或者大堆)。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
例如:
int array[] = {27,15,19,18,28,34,65,49,25,37};
同理第一个交换完成后,接着比较现在的孩子节点与根节点的大小关系,直到父亲节点都小于孩子节点,或者父亲节点没有孩子节点的情况下结束;
同理建大堆向下调整;
//向下调整
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
// 选出左右孩子中小的那一个
if (child + 1 < n && a[child + 1] < a[child])
{
++child;
}
// 如果小的孩子小于父亲,则交换,并继续向下调整
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//向上调整
void AdjustUp(int* a, int child)
{
assert(a);
int parent = (child - 1) / 2;
//while (parent >= 0)
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
创建堆
从倒数的第一个非叶子节点的 子树开始向下调整,一直调整到根节点的树,就可以调整成堆。
例如:(建大堆)
int a[] = {1,5,3,8,7,6};
建立过程如下:
堆的时间复杂度
所以时间复杂度为O(N);
堆的插入
把一个数先插入一个数组的尾上,再进行向上调整算法,直到满足堆。
void HeapPush(HP* hp, HPDataType x)
{
assert(hp);
if (hp->size == hp->capacity)
{
size_t newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDataType* tmp = realloc(hp->a, sizeof(HPDataType)*newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
hp->a = tmp;
hp->capacity = newCapacity;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp->a, hp->size - 1);
}
堆的删除
删除堆是删除堆顶的数据,将堆顶的数据根据最后一个数据交换,然后删除数组最后一个数据,再向下调整算法;
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
Swap(&hp->a[0], &hp->a[hp->size - 1]);
hp->size--;
AdjustDown(hp->a, hp->size, 0);
}