目录
堆的概念及结构
编辑
堆的实现
实现堆的接口:
堆的初始化:
堆的打印:
堆的销毁:
获取最顶的根数据:
交换:
堆的插入:(插入最后)
向上调整:(这次用的是小堆)
堆的删除:(删除根)
向下调整:(这次用的小堆)
堆排序
堆的概念及结构
如果有一个关键码的集合
K = {
, , ,
…
,},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,
1
, 2…,则称为小堆
(
或大堆
)
。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
小根堆:父亲节点大于等于孩子节点
大根堆:父亲节点小于等于孩子节点
堆的实现
实现堆的接口:
#define CRT_SECURE_NO_WARNING 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<stdbool.h>
//二叉树-堆
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);
//交换
void Swap(HPDataType* p1, HPDataType* p2);
//打印
void HeapPrint(HP* php);
//初始化
void HeapInit(HP* php);
//
void HeapInitArray(HP* php, int* a, int n);
//销毁
void HeapDestroy(HP* php);
//插入
void HeapPush(HP* php, HPDataType x);
//删除
void HeapPop(HP* php);
//返回最顶数据
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);
堆的初始化:
//初始化
void HeapInit(HP* php)
{
assert(php);
php->a = NULL;
php->size = 0;
php->capacity = 0;
}
堆的打印:
void HeapPrint(HP* php)
{
assert(php);
//最后一个孩子下标为size-1
for (size_t i = 0; i < php->size; i++)
{
printf("%d ", php->a[i]);
}
printf("\n");
}
堆的销毁:
//销毁
void HeapDestroy(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->size = php->capacity = 0;
}
获取最顶的根数据:
//获取根数据
HPDataType HeapTop(HP* php)
{
assert(php);
assert(php->size > 0);
return php->a[0];
}
交换:
void Swap(HPDataType* p1, HPDataType* p2)
{
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
堆的插入:(插入最后)
先考虑扩容,将数据插到最后,再用向上调整法。
//插入数据
void HeapPush(HP* php, HPDataType x)
{
assert(php);
//扩容
if (php->size == php->capacity)//有效元素个数和容量是否相等
{
//相等的情况分两种:1.容量为0,先扩4个sizeof 2.容量占用满了,扩2个
int newCapacity =php->capacity == 0 ? 4 : php->capacity * 2;
//返回扩容后的内存新地址 //扩容后的新大小
HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
//扩容后的新地址
php->a = tmp;
//新容量
php->capacity = newCapacity;
}
// php->size下标位置 先将x放最后,后面再调整
php->a[php->size] = x;
// 有效数据++
php->size++;
// 向上调整 //size-1为新插入数据的下标
AdjustUp(php->a, php->size - 1);
}
向上调整:(这次用的是小堆)
//向上调整 //新插入的数据下标
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 = (parent - 1) / 2;
}
else
{
break;
}
}
}
堆的删除:(删除根)
先判空,看下是否还有元素可以删除。根数据先和最后一个孩子交换位置,孩子再向下调整。
//删除
void HeapPop(HP* php)
{
assert(php);
//确保有元素可删
assert(php->size > 0);
//最后一个孩子和要删除的根交换
Swap(&php->a[0], &php->a[php->size - 1]);
//有效元素size减减,相当于删除了交换后的原来的根
--php->size;
//删除后向下调整
AdjustDown(php->a, php->size, 0);
}
向下调整:(这次用的小堆)
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
int child = parent * 2 + 1;
//n下标位置已经没有数了
while (child < n)
{
//选小的孩子往上浮(小堆)
if (child + 1 < n && a[child + 1] < a[child])
{
++child;
}
//若小的孩子都小于父,则交换
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
//交换后下来的数重新变成parent,继续向下调整
parent = child;
child = parent * 2 + 1;
}
}
}
堆排序
1.
建堆
升序:建大堆
降序:建小堆
2.
利用堆删除思想来进行排序
如,建的大堆排升序,可以用堆删除思想向下调整法将栈顶和最后一个元素交换,依次将最大的次大的......往后放,就达到了升序排列。
void HeapSort(int* a, int n)
{
//建堆 这里可以选建大堆还是小堆
for (int i = 1; i < n; i++)
{
AdjustUp(a, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}