复习排序三:堆排序
对于堆排序的简单介绍
堆排序可以分为两个阶段。在堆的构造阶段中,我们将原始数组重新组织为一个有序堆。在排序阶段,我们把堆顶元素(即数组0下标)与未排序数组的最后一个元素交换,然后再调用 sink 方法把未排序数组重新堆有序化。
1.关于堆一些定义
1)当一棵二叉树的每个节点都大于等于它的两个子节点时,它被称为堆有序
2)二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级存储(算法第四版中的代码是不使用数组第一个元素的,但我的代码中是使用了的)。
2.由上至下堆的有序化的实现
如果堆的有序状态因为某个节点变得比它的两个子节点或者某个子节点更小而被打破,那么我们可以将它与两个子节点中的较大者来比较恢复堆,交换可能会在子节点处继续打破有序状态,因此我们需要不断地用相同的办法来修复,将节点向下移动直到他的子节点比他更小了或是到达了底部。
public void sink(int[] nums,int i,int len){
//保存局部堆顶元素
int temp = nums[i];
for (int k = 2 * i + 1; k < len;k = k*2+1) {
//因为数组从0开始,所以二叉堆的左节点为2*i+1
if(k + 1 < len && nums[k] < nums[k+1]){ //让k下标指向左右节点较大的数
k += 1;
}
if(nums[i] >= nums[k]) break;//break的原因是左右子树都已经是有序堆了
if(nums[i] < nums[k]){
nums[i] = nums[k];
nums[k] = temp;
i = k;
}
}
}
3.堆排序代码实现
public static void heapSort(int[] nums){
for (int i = nums.length/2-1; i > -1; i--) {//由下至上调用sink使整个堆有序
sink(nums,i,nums.length);
}
// System.out.println(Arrays.toString(nums));
int temp;
for (int n = nums.length-1;n > 0 ;n--) {
temp = nums[n];
nums[n] = nums[0];
nums[0] = temp;
sink(nums,0,n);
}
}