数据结构初阶:二叉树(二)
本篇博客主要讲解二叉树---堆的相关知识。
1.实现顺序结构二叉树
一般堆使用顺序结构的数组来存储数据,堆是一种特殊的二叉树,具有二叉树的特性的同时,还具备其他的特性。
1.1 堆的概念和结构
堆具有以下性质:
- 堆中某个结点的值总是不大于或不小于其父结点的值;
- 堆总是一颗完全二叉树。
二叉树性质:
对于具有 n 个结点的完全⼆叉树,如果按照从上至下从左右的数组顺序对所有结点从 0 开始编号,则对于序号为 i 的结点有:
- 若 i>0 , i 位置结点的双亲序号: (i-1)/2 ; i=0 , i 为根结点编号,无双亲结点
- 若 2i+1,左孩子序号: 2i+1 , 2i+1>=n 否则无左孩子
- 若 2i+2,右孩子序号: 2i+2 , 2i+2>=n 否则无右孩子
1.2 堆的实现
堆底层结构为数组,因此定义堆的结构为:
1.2.1 头文件的包含(Heap.h)
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//堆的结构
typedef int HPDataType;
typedef struct Heap
{HPDataType* arr;int size; //有效数据个数int capacity; //空间大小
}HP;//默认初始化堆
void HPInit(HP* php);
//堆的销毁
void HPDestroy(HP* php);
void HPPrint(HP* php);
void Swap(int* x, int* y);//向上调整算法
void AdjustUp(HPDataType* arr, int child);
//向下调整算法
void AdjustDown(HPDataType* arr, int parent, int n);//堆的插入
void HPPush(HP* php, HPDataType x);
//删除堆顶的数据
void HPPop(HP* php);
//堆的删除
HPDataType HPTop(HP* php);//判空
bool HPEmpty(HP* php);
1.2.3 堆的初始化
void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
代码逻辑:
定义了一个`HPInit`的函数,用于初始化一个`HP`类型的指针`php`。函数内部首先进行了参数的有效性检查,确保`php`不为空。然后,将`php`所指向的结构体中的`arr`成员设置为`NULL`,并将`size`和`capacity`成员都设置为`0`。
1.2.4 堆的销毁
void HPDestroy(HP* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}
代码逻辑:
定义了一个 `HPDestroy` 的函数,用于销毁一个 `HP` 类型的指针 `php` 所指向的资源。函数内部首先进行参数有效性检查,确保 `php` 不为空。然后,判断 `php->arr` 是否不为空,如果不为空则使用 `free` 函数释放其占用的内存。最后,将 `php->arr` 设置为 `NULL`,并将 `php->size` 和 `php->capacity` 都设置为 `0`。
1.2.5 向上调整算法
//向上调整
void AdjustUp(HPDataType* arr, int child)
{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;}}
}
代码逻辑:
这段 代码定义了一个`AdjustUp`的函数,用于对一个数组进行向上调整操作,以构建一个堆(这里的堆可能是大顶堆或小顶堆,具体取决于比较运算符的具体实现)。
以下是对代码的详细解释:
- `int parent = (child - 1) / 2;`:通过这个公式计算出当前节点`child`的父节点`parent`的索引。
- `while (child > 0)`:只要当前节点的索引大于 0,就会进入循环进行调整。
- `if (arr[child] > arr[parent])`:这里根据堆的性质进行判断,如果当前节点的值大于父节点的值(这里假设是构建大顶堆),则交换它们的位置。
- `Swap(&arr[child], &arr[parent])`:这是一个用于交换两个元素的函数调用。
- `child = parent; parent = (child - 1) / 2;`:交换位置后,更新当前节点和父节点的索引,以便继续向上调整。
- `else { break; }`:如果当前节点的值不大于父节点的值,那么就停止调整,退出循环。总的来说,这个函数的作用是在堆的构建过程中,将一个可能不符合堆性质的子节点向上调整,以保持堆的性质。
1.2.6 堆的插入
void HPPush(HP* php, HPDataType x)
{assert(php);//判断空间足够if (php->size == php->capacity){//增容int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//直接插入php->arr[php->size] = x;//向上调整AdjustUp(php->arr, php->size);++php->size;
}
代码逻辑:
这段代码定义了一个名为`HPPush`的函数,用于向一个堆(`HP`)中插入一个元素。
以下是对代码的详细解释:
- `assert(php);`:用于检查指针`php`是否有效。
- `if (php->size == php->capacity)`:检查堆的当前大小是否等于其容量。如果是,说明需要进行扩容操作。
- `int newCapacity = php->capacity == 0? 4 : 2 * php->capacity;`:根据当前容量的情况计算新的容量。如果当前容量为 0,则新容量为 4;否则,新容量为当前容量的 2 倍。
- `HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));`:使用`realloc`函数为堆的数组重新分配内存,以扩大其容量。
- `if (tmp == NULL)`:如果`realloc`操作失败,会输出错误信息并退出程序。
- 如果扩容成功,更新堆的数组指针和容量。
- `php->arr[php->size] = x;`:将新元素直接插入到堆的末尾。
- `AdjustUp(php->arr, php->size);`:调用`AdjustUp`函数对插入的元素进行向上调整,以保持堆的性质。
- `++php->size;`:增加堆的大小。总的来说,这个函数的作用是在保证堆有足够空间的前提下,将一个元素插入到堆中,并通过向上调整来维持堆的结构。
1.2.7 判空和向下调整算法
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}
//向下调整
void AdjustDown(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;while (child < n){//大堆: <//小堆: >if (child + 1 < n && arr[child] < arr[child + 1]){child++;}//大堆:>//小堆: <if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else {break;}}
}
代码逻辑:
这段 代码中定义了两个函数:
- `bool HPEmpty(HP* php)`:这个函数用于检查一个堆(`HP`)是否为空。函数内部通过检查堆的大小是否为 0 来判断堆是否为空。如果堆的大小为 0,则返回`true`,表示堆为空;否则返回`false`,表示堆不为空。
- `void AdjustDown(HPDataType* arr, int parent, int n)`:这个函数用于对堆进行向下调整操作。函数通过比较父节点和子节点的值,将父节点与较大(或较小,具体取决于堆的类型)的子节点进行交换,以保持堆的性质。在函数中,首先找到左子节点的索引,然后通过比较左右子节点的值,找到较大(或较小)的子节点。接着,将父节点与较大(或较小)的子节点进行比较,如果子节点的值大于(或小于)父节点的值,则进行交换,并更新父节点和子节点的索引,继续进行下一轮的调整。如果子节点的值小于(或大于)父节点的值,则停止调整。总的来说,这两个函数分别实现了检查堆是否为空和对堆进行向下调整的功能。
- 堆的删除
删除堆是删除堆顶的数据,将堆顶的数据跟最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
- 向下调整算法
- 将堆顶元素与堆中最后一个元素进行交换
- 删除堆中最后一个元素
- 将堆顶元素向下调整到满足堆特性为止
1.2.8 堆顶数据的删除和堆的删除
void HPPop(HP* php)
{assert(!HPEmpty(php));//0 php->size-1Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//向下调整AdjustDown(php->arr, 0, php->size);
}
HPDataType HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}
1.3 源代码(Heap.c)
#define _CRT_SECURE_NO_WARNINGS
#include"Heap.h"void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
void HPDestroy(HP* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}
void HPPrint(HP* php)
{for (int i = 0; i < php->size; i++){printf("%d ", php->arr[i]);}printf("\n");
}
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//向上调整
void AdjustUp(HPDataType* arr, int child)
{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;}}
}void HPPush(HP* php, HPDataType x)
{assert(php);//判断空间足够if (php->size == php->capacity){//增容int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//直接插入php->arr[php->size] = x;//向上调整AdjustUp(php->arr, php->size);++php->size;
}
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}
//向下调整
void AdjustDown(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;while (child < n){//大堆: <//小堆: >if (child + 1 < n && arr[child] < arr[child + 1]){child++;}//大堆:>//小堆: <if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else {break;}}
}
void HPPop(HP* php)
{assert(!HPEmpty(php));//0 php->size-1Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//向下调整AdjustDown(php->arr, 0, php->size);
}
HPDataType HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}
2.小结
以上便是关于堆的所有内容,如果大家喜欢这篇博客,还请给博主点点赞,谢谢大家!