对堆中数据进行了插入和删除后通常会破坏原本堆的排列结构,需要通过对堆进行调整来恢复大堆或者小堆的排列
这里假设原来的堆为小堆,所有孩子节点都大于其祖先节点,逻辑结构如下
向上调整:要求从该节点的父节点开始往上是一个堆,用于堆的插入,时间复杂度为logn
堆的插入是在堆的末尾插入数据,现在插入一个值为10的节点,设该节点为child
先找到其父亲parent = (child - 1) / 2;
如果孩子的值小于父亲则交换,否则break。
交换后孩子和父亲都向上走一层child = parent; parent = (child - 1) / 2;
重复交换,直到child为根节点
10<28,交换
10<18,交换
10<15,交换
10为根节点,停止最后结果如图所示
void HeapPush(HP* php, HPDataType x) {
assert(php);
//扩容
if (php->size == php->capacity) {
HPDataType* tmp = (HPDataType*)realloc(php->arr, sizeof(HPDataType) * (php->capacity+1)*2);
if (tmp == NULL) {
perror("realloc is fail!\n");
exit(-1);
}
else {
php->arr = tmp;
php->capacity = (php->capacity + 1) * 2;
}
}
//放数据
php->arr[php->size] = x;
php->size++;
//排序
//传入 数组 和 插入的元素下标,因为size前面++了所以这里要-1
AdjustUp(php->arr, php->size - 1);
}
//向上调整:要求从该节点的父节点开始往上是一个堆,时间复杂度为logn
void AdjustUp(HPDataType* arr, int child) {
//找到parent
int parent = (child - 1) / 2;
//当孩子不为根进入循环
while (child > 0) {
//如果孩子的值小于父亲则交换,否则break
if (arr[child] < arr[parent]) {
swap(&arr[child], &arr[parent]);
//孩子和父亲都上走一层
child = parent;
parent = (child - 1) / 2;
}
else {
break;
}
}
}
向下调整:要求从该节点子节点开始往下是一个堆,用于堆的删除,时间复杂度为logn
堆的删除是删除堆顶的数据,将堆顶的数据与最后一个数据一换,然后删除数组最后一个数据,再进行向下调 ,整算法。
现在删除堆顶数据10,先将其与堆尾数据互换
再删除堆尾数据
对堆中数据进行向下调整
设根节点为parent,找到其左孩子childleft = parent * 2 + 1;右孩子childright = parent * 2 + 2;
选出值较小的孩子与parent交换
交换完后parent和child的向下走一层,parent = child; child = parent * 2 + 1;
重复交换,直到child放回最后一层(child原来就是从最后一层换上来的)
15<19,与15交换
18<25,与18交换
调整完毕
void HeapPop(HP* php) {
assert(php);
assert(php->size > 0);
//交换根和最后一个孩子,因为php->size = 元素个数 = 下一元素的插入位置,所以要减一才是末尾元素的位置
swap(&php->arr[0], &php->arr[php->size-1]);
//删除
php->size--;
//向下调整
AdjustDown(php->arr, php->size, 0);
}
//向下调整:要求从该节点子节点开始往下是一个堆,时间复杂度为logn
void AdjustDown(HPDataType* arr, int size, int parent) {
assert(arr);
//选出较大的孩子,默认左孩子大,不能选右孩子,因为右孩子可能不存在
int child = parent * 2 + 1;
//当child存在(child<size),进入循环
while (child < size) {
//查看是否有右孩子,并使用左右孩子中较大的那个
if (child + 1 < size && arr[child] < arr[child + 1]) {
child++;
}
//比较parent和child的大小,如果arr[parent]<arr[child]则交换,否则直接break
if (arr[parent] < arr[child]) {
swap(&arr[parent], &arr[child]);
//parent和child向下走一层
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}