树与二叉树堆:堆

目录

堆的概念:

堆的分类:

大堆:

小堆:

堆的实现:——以小堆为例 

堆的定义:

堆的初始化和销毁:

堆的插入: 

自下而上的交换操作:

尾插操作: ——与顺序表的尾插一样

交换函数: 

堆的删除: 

自上而下的交换操作: 

尾删操作:——和顺序表的尾删一样

获取堆顶元素:——获取根结点元素

判断堆是否存在:——判断堆的结点个数是否是零

获取堆的结点个数:

 主函数部分:

创建操作

获取堆中前K个最小元素 

打印堆

堆的意义: 

完整代码:

头文件部分:

源文件部分:

 主函数部分:


树与二叉树堆:堆_第1张图片

堆的概念:

一般是把数组的数据在逻辑结构上看成一颗完全二叉树,如下图所示。

                     树与二叉树堆:堆_第2张图片

注意:别将C语言中的堆和数据结构的堆混为一谈,本文所讲的数据结构的堆是一种完全二叉树,而C语言中的堆其实是一种内存区域的划分 

堆的分类:

小堆:小堆的数组转化为完全二叉树则就是父亲节点要比孩子节点小。

大堆:大堆的数组转化为完全二叉树则就是父亲节点要比孩子节点大。

  • 根据大小堆的特点,可以得知有序数组一定是堆,但堆不一定是有序数组 

大堆:

树与二叉树堆:堆_第3张图片

小堆:

树与二叉树堆:堆_第4张图片

堆的实现:——以小堆为例 

树与二叉树堆:堆_第5张图片 

堆的定义:

树与二叉树堆:堆_第6张图片

因为堆的底层是数组所以定义一个数组的结构体,且看似一个顺序表,但本质上并不是顺序表,因为逻辑结构是一个完全二叉树,所以插入的要求要完全按照完全二叉树的要求来看,这里就需要用到之前完全二叉树存储到数组中后,父节点和子节点再数组中的下标关系。

堆的初始化和销毁:

注意:size是表示结点的个数,而不是数组的下标,结点的个数始终是比数组的下标大1 ,所以之后的插入操作中 size 是表示需要插入的位置下标。

                                    树与二叉树堆:堆_第7张图片

堆的插入: 

  • 堆的插入和删除本质上是需要对堆的状态进行一种保持,例如,再插入前是小堆类型,那么插入后就必须是小堆类型,删除也是一样
  • 所以怎么样使得堆保持再小堆状态或者是大堆状态是插入和删除需要考虑的问题。

              树与二叉树堆:堆_第8张图片

  • 而之前说过,堆的底层是一种数组,而数组的另一种令人熟知的结构是顺序表,那么是否能够模仿顺序表的尾插进行堆的插入操作呢?
  • 答案是可以! 
  • 但,需要对这种操作进行一种改造
  • 因为尾插的数据不确定,而根据小堆的特点,父节点始终需要比子节点要小,所以需要进行比较,比较后进行子节点和父节点之间的交换
  • 但仅仅一次的交换是不够的!
  • 因为父节点之上,还有父节点的父节点,所以为了保持堆的结构,需要自下而上的进行一次判断交换操作! 

树与二叉树堆:堆_第9张图片

自下而上的交换操作:

树与二叉树堆:堆_第10张图片 

尾插操作: ——与顺序表的尾插一样

树与二叉树堆:堆_第11张图片 

交换函数: 

树与二叉树堆:堆_第12张图片 

堆的删除: 

  • 删除数据,分为两种,一种是删除根结点,一种是删除最后的结点。
  • 但是删除最后的结点其实没有任何的意义,对于堆的结构并不会发生改变,所以通常而言删除堆其实就是删除堆的根节点后,堆依旧保持大堆状态或者小堆状态。 

 其次,删除堆的根节点有一个误区,就是会沿用顺序表的头删操作,这样做虽然删除了根本节点,但是对于堆的结构却发生了不可逆转的变化,那就是父亲不是父亲,孩子不是孩子,兄弟不是兄弟。

要知道顺序表是一种连续的线性表结构,而堆是一个完全二叉树结构,二者的结构本质上是不一样的!

树与二叉树堆:堆_第13张图片

  •  而正确的做法,是将尾的结点的元素和根结点元素进行交换,交换后再进行尾删,这样既不会使得堆的父子节点关系错乱,保证了左右子树的各个节点关系正常。
  •  而在交换后进行尾删,达到删除的目的,之后在进行自下而上的交换,和自己的子节点自上而下的进行数值的交换,从而保证堆还是原来的小堆或者大堆。

进行交换要符合条件,也就是小堆或者大堆的父子关系特点

例如父节点要不子节点小——例子以小堆为例,因为要换小的,所以要换最小的那个孩子,所以需要左右孩子进行比较,交换的条件是判断孩子的结点下标是不是超出了数组长度的范围,超了就是叶节点表示交换停止了。 

  •  注意:如果不是交换左右孩子中最小的那个,那么就不是堆

自上而下的交换操作: 

树与二叉树堆:堆_第14张图片 

尾删操作:——和顺序表的尾删一样

树与二叉树堆:堆_第15张图片 

获取堆顶元素:——获取根结点元素

树与二叉树堆:堆_第16张图片 

判断堆是否存在:——判断堆的结点个数是否是零

树与二叉树堆:堆_第17张图片 

获取堆的结点个数:

树与二叉树堆:堆_第18张图片

 主函数部分:

创建操作

树与二叉树堆:堆_第19张图片

获取堆中前K个最小元素 

树与二叉树堆:堆_第20张图片 

打印堆

树与二叉树堆:堆_第21张图片 

堆的意义: 

第一是堆的排序,第二是堆的top k 排行问题

堆的 top k 排行问题:

  • 堆的top k 排行问题主要是利用了 大堆或者小堆的堆顶一定是最大或者最小值的特点,再利用了堆的删除操作,从而进行堆的一种大小排序,从而形成一种升序或者逆序的top榜单排满。
  • 再面对大量的数据时,如果要进行排列前k个最小的数值时,可以先创建一个拥有K个结点大小的小堆,随后再进行插入,将插入的数据和栈顶元素进行比较,如果比栈顶元素小,那么替换栈顶元素,随后再和栈顶下的各个结点进行比较和交换。
  • 这种做法到最后,形成了一种栈顶是最小的元素的结果,这种方法也相当于是一种末位淘汰制。 

完整代码:

头文件部分:

树与二叉树堆:堆_第22张图片 

源文件部分:

树与二叉树堆:堆_第23张图片 

 主函数部分:

树与二叉树堆:堆_第24张图片


树与二叉树堆:堆_第25张图片

你可能感兴趣的:(树与二叉树堆,数据结构,数据结构,堆,二叉树,完全二叉树)