一、树
1.1 树的概念与结构
1.2 树的相关术语
1.3 树的表示
二、二叉树
2.1 二叉树的概念与结构
2.2特殊的二叉树
满二叉树
完全二叉树
2.3 二叉树的存储结构
三、实现顺序结构二叉树
3.1 堆的概念与结构
3.2 堆的实现
Heap.h
Heap.c
默认初始化堆
堆的销毁
堆的插入
删除堆顶数据
树形结构中,⼦树之间不能有交集,否则就不是树形结构
孩⼦兄弟表⽰法:
struct TreeNode { struct Node* child; // 左边开始的第⼀个孩⼦结点 struct Node* brother; // 指向其右边的下⼀个兄弟结点 int data; // 结点中的数据域 };
⼀个⼆叉树,如果每⼀个层的结点数都达到最⼤值,则这个⼆叉树就是满⼆叉树。也就是说,如果⼀个⼆叉树的层数为 K ,且结点总数是 2 k − 1 ,则它就是满⼆叉树。
完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度为 K 的,有 n 个结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从 1 ⾄ n 的结点⼀⼀对应时称之为完全⼆叉树。要注意的是满⼆叉树是⼀种特殊的完全⼆叉树。
(假设二叉树层次为 k ,除了第 k 层外,每层结点的个数达到最大结点数,第 k 层结点个数不一定达到最大结点数)
小根堆 大根堆
以小根堆为例子:
#include #include #include #include //定义堆的结构---数组 typedef int HPDataType; typedef struct Heap { HPDataType* arr; int size;//有效的数据个数 int capacity;//空间大小 }HP; //初始化 void HPInit(HP* php); //销毁 void HPDestroy(HP* php); //堆的插入 void HPPush(HP* php, HPDataType x); //出堆,删除堆顶数据 void HPPop(HP* php); //返回堆顶数据 HPDataType HPTop(HP* php); // 判空 bool HPEmpty(HP* php);
void HPInit(HP* php) { assert(php); php->arr = NULL; php->capacity = php->size = 0; }
void HPDestroy(HP* php) { assert(php); if (php->arr) { free(php->arr); php->arr = NULL; php->capacity = php->size - 0; } }
如果要在下一个数据 “50” 到 arr【6】的位置上就不满足小堆的特性,
此时我们就要用到:堆的向上调整算法
向上调整算法 • 先将元素插⼊到堆的末尾,即最后⼀个孩⼦之后 • 插⼊之后如果堆的性质遭到破坏,将新插⼊结点顺着其双双亲往上调整到合适位置即可void swap(int* x, int* y) { int z = *x; *x = *y; *y = z; } 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 = 2 * child - 1; } 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; php->size++; Adjustup(php->arr, php->size - 1); }
如果直接删除 arr【0】,就会改变原先堆的结构,所以我么可以先先将头和尾的数据交换,在删除 arr【5】,但是又有问题出现。交换删除后的数据有可能不满足小堆的特性,此时就要用到:堆的向下调整算法
向下调整算法 • 将堆顶元素与堆中最后⼀个元素进⾏交换 • 删除堆中最后⼀个元素 • 将堆顶元素向下调整到满⾜堆特性为⽌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(php && php->size); //arr[0] arr[size-1] swap(&php->arr[0], &php->arr[php->size - 1]); php->size--; AdjustDown(php->arr, 0, php->size); }
最后测试一下代码的实现
#include"Heap.h" void Hptest() { HP hp; HPInit(&hp); int arr[] = { 17,25,60,54,30,70 }; for (int i = 0; i < 6; i++) { HPPush(&hp, arr[i]); } HPPop(&hp); } int main() { Hptest(); return 0; }
方法一:基于已有数组建堆、取堆顶元素完成排序版本
// 1、需要堆的数据结构 // 2、空间复杂度 O(N) void HeapSort(int* a, int n) { HP hp; for(int i = 0; i < n; i++) { HPPush(&hp,a[i]); } int i = 0; while (!HPEmpty(&hp)) { a[i++] = HPTop(&hp); HPPop(&hp); } HPDestroy(&hp); }
方法二:
// 升序,建⼤堆 // 降序,建⼩堆 // O(N*logN) void HeapSort(int* a, int n) { // a数组直接建堆 O(N) for (int i = (n-1-1)/2; i >= 0; --i) { AdjustDown(a, n, i); } // O(N*logN) int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDown(a, end, 0); --end; } }
上一篇:详解反向传播(BP)算法
下一篇:算法笔记——LCR