首先知道什么是完全二叉树:
如果一棵具有n个结点的深度为k的二叉树,它的每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应,
这棵二叉树称为完全二叉树。这个其实很好理解!一个数组对应的完全二叉树,即父结点为i位置的话,左孩子的结点位置为2*i+1,右孩子的结点位置为2*i+2。
然后知道什么是大根堆和小根堆:
堆是一种特殊的树形数据结构,其每个结点都有一个值,通常提到的堆都是指一颗完全二叉树,所有父结点的值小于两个子结点的值,叫小根堆;所有父结点的值大于两个子结点的值,叫大根堆。
给定一个数组,如何实现大根堆:
首先遍历数组,确保0~i的元素是大根堆,形成一个子树时,验证子结点是否比父结点小,如果大则与父结点交换,并继续与上一层的父结点比较(子结点i的父结点是(i-1)/2),直到根结点。以{2,1,3,6,0,4}为例,2为根结点,1和3分别为左右子结点,此时形成的完全二叉树右子结点比父结点大,所以二者交换,此时父结点为根结点,就不需要继续向上比较了,当前的数组修改为{3,1,2,6,0,4};接下来,6和0分别是父结点1的左右子结点,但是6比1大,所以将6与1交换位置,此时数组修改为 {3,6,2,1,0,4},由于6不是根结点,继续向上一层比较,6也比3大,所以6和3交换,此时的数组修改为{6,3,2,1,0,4};接下来4是父结点2的左孩子,4比2大,所以交换,因为4不比6大,所以不交换,此时的数组修改为{6,3,4,1,0,2}结点,遍历结束,就得到最终的大根堆。
堆排序:
利用大根堆,给定一个数组实现大根堆,然后根结点与最后一个结点交换,这样最后一个结点就是最大值,再把最后一个结点从大根堆中去掉;然后利用heapFy()重新生成大根堆,一直重复上述过程,直到大根堆只剩一个结点为止,就完成排序了。时间复杂度 O(N*logN)。
值得注意的是,Java 5 之后提供了一个优先级队列PriorityQueue,它是Java的一种集合,默认实现的就是小根堆。
public class HeapSort {
public static void heapSort(int[] arr){
if(arr == null || arr.length < 2)
return;
//遍历数组,确保0~i是大根堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr,i);
}
int heapSize = arr.length;
while(heapSize > 1){
swap(arr,0,--heapSize); //调换第一个元素和最后一个元素,将最大值放到最后
heapFy(arr,0,heapSize);
}
}
public static void heapInsert(int[] arr,int index){ //生成大根堆
//确定当前结点的值是否比其父结点大
while(arr[index] > arr[(index - 1)/2]){ //当index就是根结点时,(index-1)/2 -1/2其实也是0,二者相等while还是不执行
swap(arr,(index - 1)/2,index);
index = (index - 1)/2;
}
}
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
/**
* 当大根堆的某个数突然变小时,下面方法可以修正。
* index是改变元素的位置,heapSize是堆的范围,针对不一定堆就是整个数组的情况
*
* 具体过程是:得到index左右子结点的最大值跟index比较,如果index要小,就交换;然后继续与下一层的进行比较。
*/
public static void heapFy(int[] arr,int index,int heapSize){
int left = index*2 + 1;
while(left < heapSize){ //不取等的原因是,size从1开始。
// if(left + 1 < heapSize){
// int large = arr[left] > arr[left+1] ? left : left + 1;
// if(arr[index] < arr[large]){
// swap(arr,index,large);
// index = large;
// left = index*2 + 1;
// }else{ //如果改变后的index位置值还是比两个子结点大,则直接结束
// break;
// }
// }else{
// if(arr[index] < arr[left]){
// swap(arr,index,left);
// index = left;
// left = index*2 + 1;
// }else{
// break;
// }
//
// }
//上面写得太繁琐了
int large = left + 1 < heapSize && arr[left] < arr[left+1] ? left + 1 : left;
if(arr[index] < arr[large]){
swap(arr,index,large);
index = large;
left = index*2 + 1;
}else{ //如果改变后的index位置值还是比两个子结点大,则直接结束
break;
}
}
}
//打印数组元素
public static void printArray(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
public static void main(String[] args){
int[] arr = {2,1,3,6,0,4};
heapSort(arr);
printArray(arr);
}
}