二叉树---堆的代码实现

->有关堆的知识

        (1)堆总是一个完全二叉树。

        (2)堆分为大堆和小堆,大堆的意思是每个父亲结点都要比孩子结点更大或者等于,而小堆就是每个父亲结点都要比孩子结点更小或者等于,在大堆中最大的总是根位置的数,小堆同理类推。

       (3)在插入一个数据后,堆的形式不变他依然还是一个符合原来性质的堆。

       (4)堆是可以用来排序的,但是他并不是排序。

1.代码实现的目的

         用代码完成堆的创立,删除,增加数据,删除数据,等常用功能。

2.代码的逐步实现(以大堆为例)

      (1)堆的初始化和一些头文件

堆的形式与顺序表很相似,都是用数组来存储数据,但是一般也只有完全二叉树才使用数组来存储数据,这是因为普通的二叉树存入数组里面会导致空间利用不完全等问题。

用图的形式就是这样的一个样子:

由上图我们还可以得知,完全二叉树在数组中的存储是按层序存储的。

以下就是代码实现:

头文件等:

初始化代码:

      (2)添加数据

堆在添加数据后依然还得是一个堆,我们的size是数组中数据的个数,添加数据我们可以把它添加到下标为size的位置上面去,但是这样我们会发现想要符合堆的性质,难免遇到交换位置的问题。

这个时候我们就需要用到一些关于树中的计算问题了:

在一棵二叉树(上图仅为一个普通的树)中,将A的下标为0,B的下标为1,然后按照这样的规律类推下去。在下标的运算中我们会发现,父亲结点的总是等于孩子下标-1之后在/2得到,而孩子结点则是父亲结点的下标乘以二再加1或二:

       即:parent = ( child - 1 ) / 2;    leftchild = parent * 2 + 1;  rightchild = parent * 2 + 2;

便于交换位置我们可以单独写出一个swap函数:

然后函数的主题部分就如下实现:

下面解析一下这部分代码:

如果size == capacity,则说明数组的空间已经满了,则对它进行扩容的操作(我这里是扩大到原来的二倍)。然后在size下标位置添加新数,size的意义是反映数据个数,所以要++。最关建的是最后一个函数:

如果插入的数比它的父亲结点更大,那么必然是需要换位置的,这时候就是向上调整的过程。

这个函数需要传入新添加的数据的下标,然后利用parent = ( child - 1 ) / 2

这里有一个注意的点就是循环的条件,这里建议用child>0来控制,有人会用parent来控制,实际上这是不正确的,这个代码的意思就是如果字节点比父亲节点大,那么交换位置,交换位置之后再重新新的一轮比较,若使用parent > 0作为循环条件,当元素调整到根节点时,由于根节点的父节点索引是负数,在某些情况下可能会引发未定义行为,而且无法准确判断是否已经到达根节点。

(3)删除数据

试想删除数据我们需要删除最后的数据吗,那岂不是太简单了,直接size--就完事了,但是我们会发现这样没啥意义,所以我们删除堆顶的数据。然后让这个堆重新变成一个大堆。我们直接删除的话,会发现太难控制了,所以有一个删除的方法就是,把顶部数据和最后一个叶子数据交换位置,然后将交换后的数组再变成一个堆,最后再size--就可以了。

最核心的莫过于向下调整的函数了,这是大堆,交换之后要保持根部数据最大向下调整是必然的。由于前面的size减过一次,则传入的size就是剩余数据的个数,0则是根部数据的下标:

下面是代码实现:

向下调整的过程中需要比较父亲结点和孩子结点的大小,我们默认两个孩子中左边的孩子是较大的那一个,这样换上去之后他就是最大的了(因为原本就只有现在的根部数据不符合大堆的形式)。

这里的控制条件是child不能大于n也就是size要小于等于数组的最大下标。

(4)求堆顶数据

以下是代码实现:

(5)对这个堆判空

以下是代码实现:

(6)求有多少个数据

以下是代码实现:

(7)销毁堆

以下是代码实现:

3.结尾

由于后面几个函数的实现过于简单,便不再过多赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值