序言
最近在刷leetcode遇到一个名为【大根堆】的东西,首先我不太清楚这是什么,所以我不能改厨准确的定义;它是方法?或者一个阶梯思路?或者一个数据结构?或者是其他什么。因为这个名字广泛出现在各种题解中。比如:
既然这么多题出现【大根堆】,它应该很重要,而我又不会;所以我决定搞清楚它
堆
百度百科上说:堆通常是一个可以被看做一棵完全二叉树的数组对象
比如:数组array = [7,3,8,5,1,2]
数组array可以看做如下完全二叉树
7
/ \
3 8
/ \ /
5 1 2
是要将数组转换成二叉树?
不不不,只是把数组类比成二叉树,要不然我说8和7要调换位置,你会问为什么要调换?
什么是大根堆?
什么是大根堆:每个结点的值都大于或等于其左右孩子结点的值
比如:数组array = [7,3,8,5,1,2]
数组array大根堆可以是
8
/ \
5 7
/ \ /
3 1 2
上述二叉树是不是节点值都大于等于左右子节点?
这就是大根堆;
疑问:你这还是将数组转换为二叉树了呀。
没有啊,数组由[7,3,8,5,1,2] 变成了 [8,5,7,3,1,2]
数组还是数组,只是数组中的值位置发生了改变;
改变的规则是按照二叉树的大根堆规则来的;这样在数组添加,或者删除元素后,可以快速找到数组中最大值,最小值
总结:
找最值用大根堆,小根堆
如何构建大根堆呢?
添加
比如:数组array = [7,3,8,5,1,2],二叉树根节点位0;
构建后的大根堆位list = [0],其中0没有实际意义,表示根节点
1、取数组第一位7放入二叉树
7
list = [0,7]
2、取第2为值3放在二叉树中;
- list = [0,7,3]
- list符合大根堆吗?
7
/
3
符合,不处理
3、取数组array第3为值8放在二叉树中;
- list = [0,7,3,8]
- list符合大根堆吗?
7
/ \
3 8
不符合,8>7所以8要与7交换位置;
在二叉树视图上比较好观察,但是怎么在程序中交换他们的位置呢?
这里需要补充几个概念;
在完全二叉树中,对于非根节点x都有
父级值在array数组的下标为: Math.floor(x/2)
左子级值在array数组的下标为:2x
右子级值在array数组的下标为: 2x + 1
1
/ \
2 3
/ \ / \
4 5 6 7
所以:
7与8交换位子就是8所在的下标3,与7所在的下标1交换位置,所以数组变成了[0,8,3,7]
直观的看二叉树变成了
8
/ \
3 7
4、取数组array第4位值5放入list中
- list = [0,8,3,7,5]
8
/ \
3 7
/
5
- 不符合大根堆条件,因为5 > 3
如何调整数组呢?将5的位置与3互换;
5的下标是多少呢?是4;3的下标可以计算出来是2;所以5与3调换后得到list = [0,8,5,7,3]
8
/ \
5 7
/
3
总结
通过以上4个步骤,可以知道,在向数组中添加数据的时候,现添加到数组末尾,数组末尾表示数组的叶子节点;将该数据放在叶子节点后,将该值与该值的父级节点值对比,如果该值大于父级节点值,交换两个位置;参考步骤3、4
因为从叶子节点向上交换,所以该值也父级节点交换后,要判断交换后的父级节点与父级的父级节点比较,直至比较到根节点或者小于父级节点;
删除
现在list = [0,8,5,7,3]
与list对应的二叉树如下:
8
/ \
5 7
/
3
获取数组最大值,返回array[1]即可;
删除数组最大值呢?
首先:将最后一个节点值与第一个节点子调换;调换后如下
list = [0,3,5,7,8]
3
/ \
5 7
/
8
第2步:删除叶子节点;list.pop()
list = [0,3,5,7]
3
/ \
5 7
第3步:重新构建大根堆
在这个例子中,3应该与左子节点交换还是应该去右子节点交换?这个是关键;
假如3与左子节点交换,交换后
5
/ \
3 7
不行吧,还是没构成大根堆,因为7>5
假如3与右子节点交换,交换后
7
/ \
5 3
符合大根堆;
通过上述两个假设,可以到的一个结论,如果根节点与子节点交换,可以重新构成大根堆,则与该子节点交换
了解以上内容,可以编辑代码
手写堆
先搭个架子
class Heap {
constructor(compare) {
this.list = [0] //数组,存放数据
this.compare =
typeof compare === 'function' ? compare : this.defaultCompare
}
//控制堆升序排列还是降序排列
defaultCompare(a, b) {
return a > b
}
isEmpty()