堆排序主要是两个过程:1.创建堆,
2.每次交换堆顶和最后一个元素的值,调整堆,堆顶元素即为最大值
需要注意的是:创建堆的过程中,是自下而上进行调整,而且在调整的过程中包含了自上而下的调整。因为在自下而上的调整中,若是交换了某个左孩子或者 右孩子与其父节点的值,可能会造成其相应的左子树或者右子树不满足最大堆的约束,所以,以此左孩子或者右孩子为根节点的左子树或者右子树,需要自上 进行调整。这个地方需要注意。
还需要注意的一点内容就是,我们在整个堆排序的过程中。只是在对数组A中的元素交换来交换区,并不涉及一个堆的实际物理存储。只不过是对A中的具体元 应该怎么交换的一种具体的图像形式的表示而已。
堆排序的实质是在每次堆调整中,找到数组中的最大值放到堆顶(数组第一个元素),然后交换到堆中最后一个节点(数组最后一个元素),然后堆的规模减 小一(即数组最后一个元素(已经是最大值)不参与到下一次的堆调整过程中),而每次寻找最大值的过程,就是堆进行调整的过程。
下面是具体的代码实现,循环过程中的边界都是一步步调试出来的,具体的代码功能,可参见注释。
package Sort;
import java.util.*;
public class HeapSort {
public int[] heapSort(int[] A, int n) {
//1.建堆
A = createHeap(A,n);
//2.将堆顶记录和堆中最后一个记录交换
for(int i=n;i>1;i--){
A[0] = A[0] + A[i-1];
A[i-1] = A[0] - A[i-1];
A[0] = A[0] - A[i-1];
//3.筛选法调整堆,堆中记录减少一个
A = adjustHeap(A,0,i-1); //从堆顶向下调整,所以传入0,每次调整数组个数减一
}
//4.重复第二步
return A;
}
public int[] createHeap(int[] A,int n){
for(int i=(n-2)/2;i>=0;i--){//自上而下进行堆调整,自上而下,非叶子节点的个数为(n-2)/2,因为数组索引从零开始,所以减2,只从最后一个非叶子节点调整就行。
//对这棵子数进行调整,使其为最大堆
A = adjustHeap(A,i,n);
}
return A;
}
public int[] adjustHeap(int[] A,int k,int n){ //n为节点的总个数,k为需要调整的非叶子节点,根据K可以求得其左孩子和右孩子
if(2*k+1>=n) return A;
int left = 2*k+1;
int right = 2*k + 2;
int max = left;//假设左孩子的值是最大的
if(right<n&&A[right]>A[left]){
max = right; //如果右孩子的值比左孩子的值还要大,则令 max = right
}
if(A[k]<A[max]){ //如果根节点的值不是最大值,则与最大的孩子值进行交换
A[k] = A[max] + A[k];
A[max] = A[k] - A[max];
A[k] = A[k] - A[max];
A = adjustHeap(A,max,n); //其左孩子或者右孩子与根节点交换后,还得重新调整其子数,即,若是与左孩子交换,则调整左子树,如与右孩子交换,则调整右子树。
}
return A;
}
public static void main(String[] args) {
int[] a = {3,5,8,9,1,2,3};
HeapSort hs = new HeapSort();
a = hs.heapSort(a, 7);
System.out.println(Arrays.toString(a));
}
}