目录
1、堆的概念
2.堆的存储方式
3、堆的创建
3.1、堆向下调整
3.2、堆的创建
1、堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。
2.堆的存储方式
堆在逻辑上是一棵完全二叉树,在物理上是以层序的规则采用顺序的方式高效存储的。
将元素存储到数组中后,假设i为节点在数组中的下标,根据树的性质则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子
3、堆的创建
3.1、堆向下调整
对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?
由图可知,根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可。
(堆向下调整必须满足大部分结点已满足堆的性质,只需要调整小部分即可)
向下过程(以小堆为例):
1.先判断parent是不是叶子结点(即判断有没有左孩子(叶子结点满足堆的性质))
2.判断有没有右孩子,如果有,找出值最小的那个结点与parent比较,如果比parent小,则交换
3.如果没有右子树,判断parent与左孩子的大小
4.循环检查调整完后,剩余子树是否满足堆的性质
递归实现:
//传入数组以及待调整的结点的下标
public static void adjustDown递归(int[] arr,int parentIdx){
int childLeftIdx=2*parentIdx+1;
int size=arr.length;
//1.说明没有左孩子,是一个叶子结点
if(childLeftIdx>=size){
return;
}
int childRightIdx=2*parentIdx+2; //定义右孩子
int minIdx=childLeftIdx; //先假定左右孩子中最小的是左孩子
//如果右子树存在且右子树比左子树小,重置最小节点
if(childRightIdx<size&&arr[childLeftIdx]>arr[childRightIdx]){
minIdx=childRightIdx;
}
//比较最小值结点与待调整结点的大小
if(arr[parentIdx]<arr[minIdx]){
return;
}
if(arr[parentIdx]>arr[minIdx]){
int tmp=arr[parentIdx];
arr[parentIdx]=arr[minIdx];
arr[minIdx]=tmp;
}
//再递归判断被交换的结点是否满足堆的性质
adjustDown递归(arr,minIdx);
}
非递归实现:
//非递归实现
//传入数组及待调整的下标
public static void adjustDown(int[] arr,int parentIdx){
//先判断是不是叶子结点
int size=arr.length;
while(2*parentIdx+1<size){
int minIdx=2*parentIdx+1; //假定最小值结点是左孩子
int childRightIdx=2*parentIdx+2;
//更新最小值下标
if(childRightIdx<size&&arr[2*parentIdx+1]>arr[childRightIdx]){
minIdx=childRightIdx;
}
if(arr[parentIdx]<arr[minIdx]){
return;
}
if(arr[parentIdx]>arr[minIdx]){
int tmp=arr[parentIdx];
arr[parentIdx]=arr[minIdx];
arr[minIdx]=tmp;
}
//更新下一个要调整的结点下标
parentIdx=minIdx;
}
}
3.2、堆的创建
堆向下调整需要满足的条件太过于苛刻,对于一个无序的数组来说显然不合适,那么该如何来调整呢?
找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整
以建小堆为例:{8,3,6,7,5,1}
代码如下:
public static void createHeap小堆(int[] arr){
//找到最后一个非叶子结点,从该结点一直往前到根节点,对每一个遇到的结点向下调整
int size=arr.length;
int lastIdx=(size-1-1)/2; //就是找最后一个结点的父节点
for(int i=lastIdx;i>=0;i--){
adjustDown(arr,i);
}
}
建堆的时间复杂度:O(n)