对于堆排序,首先我们要清楚双亲和左右孩子的下标关系:
已知双亲下标(parent)
左孩子的下标: 2parent+1
右孩子的下标: 2parent+2
已知左/右孩子下标
双亲下标:(child-1)/2
对堆的理解:
1.堆逻辑上是一颗完全二叉树
2.堆物理上是保存在数组中
3.满足任意结点的值都大于其子树中结点的值,叫做大堆/大根堆/最大堆,反之,小堆/小根堆/最小堆
任意给定一个数组只能逻辑上看成是二叉树,如果要看成是堆,就要建堆,大堆或者小堆都离不开向下调整的过程。
对于向下调整的操作,前提是:左右子树已经是一个堆,才能调整
说明:1.array代表储存堆的数组
2.size代表数组中被视为堆数据的个数
3.index代表要调整位置的下标
4.left代表index左孩子下标
5.right代表index右孩子的下标
代码如下:
import java.util.Arrays;
public class HeapDemo {
//堆排序
public static void main(String[] args) {
int[] array={5,2,7,4,9,7,0,6,2,1,23,15,16};
System.out.println( Arrays.toString(array));
int size=array.length;
createHeapBig(array,size);
System.out.println(Arrays.toString(array));
createHeapSmall(array,size);
System.out.println(Arrays.toString(array));
}
private static void createHeapSmall(int[] array, int size) {
for(int i=(size-1)/2;i>=0;i--){
shiftDown2(array,size,i);
}
}
private static void shiftDown2(int[] array, int size, int index) {
int left=2*index+1;
while(left<size){
int min=left;
int right=index*2+2;
if(right<size){//只用判断一次所以不能用while,只能用if
if(array[right]<array[left]){
min=right;
}
}
if(array[index]<=array[min]){
break;
}
swap(array,min,index);
//一定要检查min的堆结构是否被破坏
index=min;
left=2*index+1;
}
}
//建大堆
private static void createHeapBig(int[] array,int size) {
for(int i=(size-1)/2;i>=0;i--){//从最后一个非叶子节点的子树开始调整
shiftDown(array,size,i);
}
}
private static void shiftDown(int[] array, int size,int index) {//向下调整
int left=2*index+1;
while(left<size){
int max=left;
int right=2*index+2;
if(right<size){
//比较左右孩子谁是max
if(array[right]>array[left]){
max=right;
}
}//走到这里找到了孩子中最大的(得到max的位置)
//继续判断array[max]和array[index]的大小
if(array[max]<=array[index]){
break;
}
swap(array,max,index);
index=max;//交换完之后可能max所在的结构被破坏
left=2*index+1;//更新left(左孩子)的值,继续循环判断
}
}
private static void swap(int[] array, int max, int index) {
int t=array[max];
array[max]=array[index];
array[index]=t;
}
}