1.堆的概念

将一个关键码的集合K = {k0 , k1,k2,k3……kn-1}把他所有元素按完全二叉树的存储方式放在一个一维数组中,并且满足双亲节点大于孩子节点,或者双亲节点小于孩子节点将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

1.2堆的性质

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树

2.堆的实现及其接口详解

2.1Heap.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef int Datatype;typedef int (*func)(Datatype left, Datatype right);typedef struct Heap {Datatype* arr;int capacity;int size;func Comper;
}Heap;//堆的创建 用回调函数判断是要创建小堆还是大堆
void HeapCreate(Heap* p, Datatype *arr, int size, func Comper);//向上调整
void HeapAdjustup(Heap* p, int child);//向下调整
void HeapAdjustDown(Heap* p, int parent);//堆的插入
void HeapPush(Heap* p, Datatype data);//堆的移除
void HeapErase(Heap* p);//获取堆顶元素
Datatype HeapTop(Heap* p);//获取堆的长度
int HeapSize(Heap* p);//堆是否为空
int HeapEmpty(Heap* p);//堆的销毁
void HeapDestroy(Heap* p);//堆的扩容
void HeapCheakCapacity(Heap* p);

2.2Heap.c

#include "Heap.h"
//交换函数
void Swap(Datatype* left, Datatype* right) {Datatype temp = *left;*left = *right;*right = temp;}int Max(Datatype left, Datatype right)
{return left > right;
}int Min(Datatype left, Datatype right)
{return left < right;
}
//只是在除了根节点以外 他的两个子树也是堆结构的情况下才成立
//而且你要小堆我得改一次大于号,远没有回调函数那样的便捷
//回调函数忘记了可以自己手动实现一次qsort//冒泡排序
void BobSort(Datatype* arr,int size) {for (int i = 0; i < size - 1; i++) {for (int j = 0; j < size - 1 - i; j++) {if (arr[j - 1] > arr[j]) {Swap(&arr[j - 1], &arr[j]);}}}
}
//向下调整
void HeapAdjustDown(Heap* p, int parent)
{int child = (parent << 1) + 1;int size = p->size;while (child < size) {//因为我么对parent * 2 + 1找到的是这个节点对应的左孩子//要是我右孩子还小呢//利用回调函数找到更小的那一个将child指针移动到右孩子那块if (child + 1 < size && p->Comper(p->arr[child + 1], p->arr[child])) {child += 1;}//是否满足堆的特性if (p->Comper(p->arr[child], p->arr[parent])) {Swap(&p->arr[parent], &p->arr[child]);parent = child;child = (parent << 1) + 1;}else {return;}}
}//向上调整
void HeapAdjustup(Heap* p, int child)
{int parent = (child - 1) >> 1;while (child) {if (p->Comper(p->arr[child], p->arr[parent])) {Swap(&p->arr[child], &p->arr[parent]);child = parent;parent = (child - 1) >> 1;}else {return;}}
}//堆的扩容
void HeapCheakCapacity(Heap* p)
{assert(p);//判断是否需要扩容if (p->size == p->capacity) {//把容量变成原来的两倍int NewCapaty = p->capacity << 1;//从堆上开辟新的内存空间Datatype* temp = (Datatype*)malloc(sizeof(Datatype) * NewCapaty);if (NULL == temp) {assert(0);return;}//把原来的数据拷贝memcpy(temp, p->arr, sizeof(Datatype) * p->size);//释放旧空间改变指针指向free(p->arr);p->arr = temp; p->capacity;}
}//堆的创建
// 创建堆的时候需要用到向下调整
void HeapCreate(Heap* p, Datatype *arr, int size, func Comper)
{assert(p);//动态申请内存完成堆的初始化p->arr = (Datatype*)malloc(sizeof(Datatype) * size);//检测是否成功开辟空间if (NULL == p->arr) {assert(0);return;}//更新容量p->capacity = size;//把你要调整的数据放在你创建的堆里面memcpy(p->arr, arr, sizeof(Datatype) * size);p->size = size;p->Comper = Comper;for (int root = (size - 2) / 2; root >= 0; root--){HeapAdjustDown(p, root);}
}//插入
void HeapPush(Heap* p, Datatype data)
{HeapCheakCapacity(p);p->arr[p->size] = data;p->size++;HeapAdjustup(p, p->size - 1);
}void HeapErase(Heap* p)
{if (HeapEmpty(p)) {return;}Swap(&p->arr[0], &p->arr[p->size - 1]);p->size--;HeapAdjustDown(p, 0);}//获取堆顶元素
//因为我们是顺序表构建的堆
//那他的0号下标必是root
Datatype HeapTop(Heap* p)
{assert(p);return p->arr[0];
}//获取堆的长度
int HeapSize(Heap* p)
{assert(p);return p->size;
}//检验堆是否为空
int HeapEmpty(Heap* p)
{assert(p);return 0 == p->size;
}//堆的销毁
void HeapDestroy(Heap* p)
{assert(p);//看是否不为空 然后就直接free 最后更新堆的数据if (p->arr) {free(p->arr);p->arr = NULL;p->capacity = 0;p->size = 0;}
}int main() {int arr[] = { 49, 27, 37, 65, 28, 34, 25, 15, 18, 19 };Heap p;HeapCreate(&p, arr, sizeof(arr) / sizeof(arr[0]), Min);printf("top = %d\n", HeapTop(&p));printf("size = %d\n", HeapSize(&p));HeapPush(&p, 10);printf("top = %d\n", HeapTop(&p));printf("size = %d\n", HeapSize(&p));HeapErase(&p);printf("top = %d\n", HeapTop(&p));printf("size = %d\n", HeapSize(&p));HeapDestroy(&p);
}

2.3接口实现以及算法详解

2.3.1向下调整算法

//向下调整
void HeapAdjustDown(Heap* p, int parent)
{int child = (parent << 1) + 1;int size = p->size;while (child < size) {//因为我么对parent * 2 + 1找到的是这个节点对应的左孩子//要是我右孩子还小呢//利用回调函数找到更小的那一个将child指针移动到右孩子那块if (child + 1 < size && p->Comper(p->arr[child + 1], p->arr[child])) {child += 1;}//是否满足堆的特性if (p->Comper(p->arr[child], p->arr[parent])) {Swap(&p->arr[parent], &p->arr[child]);parent = child;child = (parent << 1) + 1;}else {return;}}
}
  1. 我们知道对于二叉树双亲节点乘2+1就是本双亲节点的左孩子。
  2. 假如我现在要创建一个小堆结构,但是我的双亲节点比我两个子节点都大,那就应该有两个指针一个找到最小的孩子,一个指向双亲,然后交换。
  3. 先要判断参数合法性,也就是我有没有右孩子要是我的左孩子++,大于size了那就是没有右孩子了。
  4. 在创建堆的时候我们提供了一个函数指针这样我们就可以根据选用不同的函数进行大堆还是小堆的创建了,这就是一个回调函数,这里就是检验是否右孩子比我左孩子小,检测是然后将这个指针进行移动。
  5. 再往下要检验是否满足你要创建的堆的特性,假如我现在是要创建小堆双亲节点小于两个子节点,那就不交换了,但是发现我比你最小的都大那就你俩交换。
  6. 交换完成之后原来的parent指针移动到子节点,对这个子节点进行运算找到他的左孩子。
  7. 我们的一系列操作都是在循环中完成的,那什么时候结束循环呢,那就是child长于size就终止了被。

2.3.2向上调整算法

//向上调整
void HeapAdjustup(Heap* p, int child)
{int parent = (child - 1) >> 1;while (child) {if (p->Comper(p->arr[child], p->arr[parent])) {Swap(&p->arr[child], &p->arr[parent]);child = parent;parent = (child - 1) >> 1;}else {return;}}
}
  1. 跟向下调整简直是异曲同工,你从传参就能看出来,向下调整穿的是parent,也就是你要通过基本性质,获得child这个刚好是反过来,通过child获得parent。
  2. 还是先判断是否满足堆,在交换,循环的出口就是root那块了

2.3.3堆的扩容

//堆的扩容
void HeapCheakCapacity(Heap* p)
{assert(p);//判断是否需要扩容if (p->size == p->capacity) {//把容量变成原来的两倍int NewCapaty = p->capacity << 1;//从堆上开辟新的内存空间Datatype* temp = (Datatype*)malloc(sizeof(Datatype) * NewCapaty);if (NULL == temp) {assert(0);return;}//把原来的数据拷贝memcpy(temp, p->arr, sizeof(Datatype) * p->size);//释放旧空间改变指针指向free(p->arr);p->arr = temp; p->capacity;}
}
  1. 这个就跟顺序表的扩容是一样的,就是开辟新空间释放旧空间,然后指针指向新空间
  2. 最后在进行数据更新。

2.3.4堆的创建

//堆的创建
// 创建堆的时候需要用到向下调整
void HeapCreate(Heap* p, Datatype *arr, int size, func Comper)
{assert(p);//动态申请内存完成堆的初始化p->arr = (Datatype*)malloc(sizeof(Datatype) * size);//检测是否成功开辟空间if (NULL == p->arr) {assert(0);return;}//更新容量p->capacity = size;//把你要调整的数据放在你创建的堆里面memcpy(p->arr, arr, sizeof(Datatype) * size);p->size = size;p->Comper = Comper;for (int root = (size - 2) / 2; root >= 0; root--){HeapAdjustDown(p, root);}
}
  1. 这里就是用到了向下调整算法了,先开辟新空间,拷贝数据。
  2. 最后在进行数据调整,通过对size的改变进行对赋值改变。

这里用这个数组进行堆的创建

int arr[] = { 49, 27, 37, 65, 28, 34, 25, 15, 18, 19 };

2.3.5堆的插入

//插入
void HeapPush(Heap* p, Datatype data)
{HeapCheakCapacity(p);p->arr[p->size] = data;p->size++;HeapAdjustup(p, p->size - 1);
}
  1. 先查看是否要扩容之后在进行数据的放入。
  2. 放入数据都是在size位置放的,在进行向上调整

还是刚刚这个数组

int arr[] = { 49, 27, 37, 65, 28, 34, 25, 15, 18, 19 };

我插入一个99

2.3.6堆的删除

void HeapErase(Heap* p)
{if (HeapEmpty(p)) {return;}Swap(&p->arr[0], &p->arr[p->size - 1]);p->size--;HeapAdjustDown(p, 0);}
  1. 删除堆是删除堆顶的数据。
  2. 先检验是否为空为空就不进行操作了
  3. 堆顶跟最后一个进行交换
  4. 在向下调整算法。

还是刚刚的数组

2.3.7堆顶元素获取

//获取堆顶元素
//因为我们是顺序表构建的堆
//那他的0号下标必是root
Datatype HeapTop(Heap* p)
{assert(p);return p->arr[0];
}

2.3.8堆顶长度获取

//获取堆的长度
int HeapSize(Heap* p)
{assert(p);return p->size;
}

2.3.9堆是否为空

//检验堆是否为空
int HeapEmpty(Heap* p)
{assert(p);return 0 == p->size;
}

2.3.10堆的销毁

//堆的销毁
void HeapDestroy(Heap* p)
{assert(p);//看是否不为空 然后就直接free 最后更新堆的数据if (p->arr) {free(p->arr);p->arr = NULL;p->capacity = 0;p->size = 0;}
}

以上代码用的都是跟顺序表一个套路,我在此就不赘述了,顺序表还不会写的同学可以移步这篇文章

数据结构 严薇敏 顺序表的实现(增 删 改)及其使用方法详解

数据结构 严薇敏 堆 的实现及其使用方法详解相关推荐

  1. 数据结构 严薇敏 栈 的实现及其使用方法详解

    目录 1.栈 1.1栈的概念及结构 1.2栈的实现 1.3接口以及实现 Stack.h Stack.c 栈的初始化 入栈 出栈 获取栈顶元素 获取栈中有效元素的个数 检测栈是否为空 栈的销毁 扩容 测 ...

  2. 数据结构 严薇敏 队列 的实现及其使用方法详解

    目录 1.队列 1.1队列的概念及结构 1.2队列的实现 1.3接口以及实现 Queue.h Queue.c 申请一个队列的节点 初始化队列 入队列 出队列 查找队头元素 查找队尾元素 获取队列长度 ...

  3. 数据结构 严薇敏 顺序表的实现(增 删 改)及其使用方法详解

    时间复杂度 数据结构 时间复杂度和空间复杂度 目录 1.线性表 2.顺序表 2.1概念及结构 2.2 接口实现 SeqList.h SeqList.c 2.2.1初始化链表以及销毁链表的实现 初始化顺 ...

  4. c语言栈是什么线性表,数据结构严薇敏——栈的顺序存储(C语言)

    栈是限定只能在表尾进行插入和删除操作的线性表. 栈的特点是后进先出. 它的顺序数据结构定义为 typedef struct SQSTACK { ElemType *base; ElemType *to ...

  5. Mr.J--C语言头函数的建立(附严薇敏《数据结构》线性表代码)

    如何正确编写 C 语言头文件和与之相关联的 c 源程序文件 查看此文章需要有一定的C语言编程基础 首先就要了解它们的各自功能.要理解C 文件与头文件(即.h)有什么 不同之处,首先需要弄明白编译器的工 ...

  6. wringPi 初始化GPIO 为上拉_敏矽微电子Cortex-M0学习笔记04-GPIO详解及应用实例

    前面我们已经对敏矽微电子的基于cortex m0内核的ME32F030R8T6的基本功能做了介绍,然后详细讲解了开发环境MDK的安装,pack包的安装,工程的建立及程序的仿真,紧接着讲解了ME32F0 ...

  7. Java堆和栈的区别/联系详解

    Java堆和栈的区别/联系详解 关于Java中堆栈内存的知识,算是基础知识,和C语言中的指针有一些类似,面试中也经常会被问到,特别是跟Java和C都有关的开发工作. 一.堆栈的联系 在Java中,内存 ...

  8. 【数据结构基础整理】图--06:克鲁斯卡尔算法详解

    详解最小生成树中的克鲁斯卡尔算法 0x01.关于克鲁斯卡尔算法 Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表.克鲁斯卡尔算法主要针对边集数组展开. ...

  9. 数据结构 严慰敏(C语言版第2版)【习题答案】

    文章目录 前言 第1章 绪论 第2章 线性表 第3章 栈和队列 第4章 串.数组和广义表 第5章 树和二叉树 第6章 图 第7章 查找 第8章 排序 前言 数据结构(C语言版第2版)[习题答案] 第1 ...

最新文章

  1. iOS之深入解析AFNetworking的底层原理
  2. Linux系统中df与du命令查看分区大小
  3. TCP/IP 通信示例
  4. Java原子类中CAS的底层实现,java高级面试笔试题
  5. Java-泛型T T与T的用法
  6. Python+OpenCV:模板匹配(Template Matching)
  7. zabbix监控之邮件报警通知
  8. COMSOL吸附模拟
  9. 原型工具XSTAR与AXURE对比
  10. salve mysql_mysql 同步实现, master-salve
  11. 信息化、数字化、数智化
  12. python是什么语言编写的程序称为_Python 学习(一)【Python语言简介-Python是什么】...
  13. 腾讯人口密度热力图_从腾讯位置大数据,看中国的超级城市
  14. 斐讯路由器设置linux,Windows10系统怎么给斐讯K3路由器开启Telnet
  15. 计算机组成原理(5)CPU功能 控制器/运算器/寄存器/操作控制器、时序发生器 指令周期 方框图 微程序 流水CPU 三种相关性
  16. HTML5 Canvas编写五彩连珠(2):画图
  17. LTE小区搜索-物理小区ID和同步信号PSS、SSS
  18. OpenGL学习(九)阴影映射(shadowMapping)
  19. iOS 常用的rgb颜色表-精华版
  20. ESP8266与PS2通信

热门文章

  1. [Vue]@keyup.enter不起作用
  2. 宝岛眼镜全员MCN,玩转私域kol
  3. 百度识图上线,体验以图搜图
  4. java 集合封装树形结构
  5. 2020计算机专业保研夏令营面经:北航计算机
  6. 手机的 32K,26M时钟电路作用 (转载于 52rd zsqt8888的专栏)
  7. rap2服务端delos环境搭建
  8. 华为南研所2015年面试经历总结
  9. linux 在指定区域分配内存 c语言,C语言动态内存分配:(一)malloc/free的实现及malloc实际分配/释放的内存...
  10. __dirname和__filename