堆(heap)的概念
- 逻辑上是一颗完全二叉树;
- 物理上保存在数组中;
- 满足任意结点的值都大于其子树中结点的值称为大堆/大根堆/最大堆;
- 反之则为小堆/小根堆/最小堆;
- 堆的作用:快速找集合中最大值。
堆的向下调整
前提:
左右子树已经是一个堆,才能调整
说明:
- array 代表存储堆的数组
- size 代表数组中被视为堆数据的个数
- index 代表要调整位置的下标
- left 代表 index 左孩子下标
- right 代表 index 右孩子下标
- min 代表 index 的最小值孩子的下标
操作过程(以小堆为例):
- index如果已经是叶子结点,则整个调整过程结束;
(1) 判断index位置有没有孩子;
(2)因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子;
(3)因为对的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即left>=size越界。 - 确定left或right,谁是index的最小孩子min;
(1)如果右孩子不存在,则min=left;
(2)否则,比较array[left]和array[right]值的大小,选择小的为min。 - 比较array[index]的值和array[min]的值,如果array[index]<=array[min],则满足堆的性质,调整结束;
- 否则,交换 array[index] 和 array[min] 的值;
- 然后因为 min 位置的堆的性质可能被破坏,所以把 min 视作 index,向下重复以上过程。
时间复杂度分析:
最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度即时间复杂度为 O(log(n))。
图示:
代码:
import java.util.*;
public class Heap {
public static void shiftDownSmall(int[] array,int size,int index){
//向下调整(小堆)
int left=2*index+1;
while(left<size) {
int right = left + 1;
int min = left;
if (right < size&&array[right] < array[left]){
min = right;
}
if(array[index]>array[min]){
swap(array,index,min);
index=min;
left=2*index+1;
}else{
break;
}
}
}
public static void shiftDownBig(int[] array,int size,int index){
//向下调整(大堆)
while (2 * index + 1 < size) {
int m = 2 * index + 1;
if (m + 1 < size && array[m + 1] > array[m]) {
m++;
}
if (array[index] >= array[m]) {
break;
}
swap(array, index, m);
index = m;
}
}
private static void swap(int[] array, int i, int j) {
int t=array[i];
array[i]=array[j];
array[j]=t;
}
public static void main(String[] args) {
int[] small={27,15,19,18,28,34,65,49,25,37};
shiftDownSmall(small,small.length,0);
System.out.println(Arrays.toString(small));
int[] big = { 100, 10, 88, 25, 40, 80, 70,18};
shiftDownBig(big, big.length, 1);
System.out.println(Arrays.toString(big));
}
}