在学堆排序之前得先明白什么是完全二叉树。
完全二叉树就是从上到下,从左到右中间没有间隔的二叉树。
例如
这样可以称为完全二叉树,
这样就不行了,
那么在了解了什么是完全二叉树之后,我们就可以把数组展开成一个完全二叉树。
就像这样。
我们再来了解一个概念【大根堆】
大根堆就是父节点是要大于子节点的,左右兄弟节点没有要求。
那么一个数组如何转化为大根堆呢?
例如数组{1,4,3,2,7,9},我们假设从数组中一个一个数拿出来进行大根堆。
先拿出来1,就一个数肯定满足大根堆
4进来的时候,不满足大根堆,所以需要交换1和4
然后3再进来
满足大根堆,继续。
2进来之后不满足大根堆,交换1和2
然后再7进来,同样不满足大根堆,继续交换。
交换一次之后任然不满足大根堆,7继续交换
后面的都是一样操作的。代码如下
public static void heapInsert(int[] arr,int index) {
while(arr[index]>arr[(index - 1)/2]) {//父节点的位置为(i-1)/2
swap(arr,index,(index-1)/2);
index = (index-1)/2;
}
}
这是来了一个数要往二叉树的上面走的,那如果是需要往下面走的又是怎样的呢?
//判断是否继续往下走
public static void heapify(int[] arr,int index,int heapSize) {
int left = index * 2 + 1;//左孩子
while(left<heapSize) {
//左孩子要在heapSize范围之内,在进行排序时,范围之外的都是已经排好序的了。
int largest = left + 1<heapSize && arr[left+1] > arr[left]? left+1:left;
//先找左右孩子中最大的那个,右孩子要最大,要满足右孩子存在且比左孩子大。
largest = arr[largest] > arr[index] ? largest : index;
//孩子中的最大值来和父节点对比。
if(largest == index) {
//如果最大的就是父节点,那么就不需要进行换位
return ;
}
//如果最大是子节点,那么就需要更换值,并继续进行下一轮的判断。
swap(arr,largest,index);
index = largest;
left = index * 2 + 1;
}
}
那这个东西有什么用呢?
假设一个数组满足大根堆,那么根节点一定就是最大的值,
让根节点和最后一个节点交换,那么数组最后一个元素就是最大值了,
然后让根节点进行从上往下走,直到满足大根堆,然后再让根节点跟数组的倒数第二个元素进行交换,
这样循环下来,最终数组就是有序的了。
public static void heapSort(int[] arr) {
if(arr == null || arr.length<2) {
return ;
}
//效率较低
for(int i = 0;i<arr.length;i++) {
heapInsert(arr,i);
}
//变成大根堆
//效率较高
// for(int i=arr.length-1;i>0;i--) {
// heapify(arr,i,arr.length);
// }
//从最后一个元素开始往上走,最终也能够成为大根堆。
int heapSize = arr.length;
swap(arr,0,--heapSize);
//heapSize表示二叉树的范围,一旦确定了在数组中的位置,就不再划分到二叉树去了
while(heapSize>0) {
heapify(arr, 0, heapSize);
swap(arr,0,--heapSize);
}
}