堆是一种特殊的完全二叉树,满足每一个结点的值比左右子树的所有结点都大(大顶堆)/小(小顶堆)。由于是完全二叉树,我们不需要用借助指针来存储树,用一个数组即可。
如 a = [1,2,3,4,5,6]表示的树:
a[i] 的左节点为 a[2*i+1],右结点为 a[2*i+2]。所以对一个数组使用堆排序在原数组上进行就可以了。
import java.util.Arrays; public class HeapSort { public static void main(String[] args) { int[] a = {2,3,1,5,4,6,9,8}; HeapSort hs = new HeapSort(); hs.heapSort(a); System.out.println(Arrays.toString(a)); } public void heapSort(int[] a) { int len = a.length; for(int i = len/2;i >= 0;i--) { //i初始指向倒数第二层的最右结点,对每个结点依次进行调整,构造大顶堆 adjust(a,i,len); } for(int i = len-1; i > 0;i--) { //构造出大顶堆后,把堆顶(最大值)与堆尾(数组末尾)元素交换,即把最大值放到数组末尾。然后再构建一次 swap(a,0,i); adjust(a,0,i); // 每交换一次,就需要调整一次。并堆长度置为i,大于i的下标的元素将不会被作为堆中元素参与调整,不然又乱了 } } public void adjust(int[] a,int i,int len) { //i表示从哪个结点开始调整,len表示堆的长度,即还没排好的那段数组元素 int temp = a[i]; for(int k = 2*i+1;k < len;k = 2*k+1) { //k指向i的子结点,并且小于堆长度 if(k+1 < len && a[k] < a[k+1]) { k++; // k 指向 i 的子结点中最大的那个 } if(a[k] > temp) { //如果最大的子结点 i 比自身大,则更新i结点的值 a[i] = a[k]; i = k; //i 指向 与自己做了交换的子结点的原位置,即下移一层,为下一次循环比较做准备。i变成新的父结点 } else break; //若子结点没有比自己大的说明此父结点已经满足大顶堆,无需调整 a[i] = temp; //若确实更新了父结点,i已经指向了下一层子结点,则需要交换,把子结点值更新为父结点的值 } } public void swap(int[] a,int i,int j) { //交换堆顶堆尾 a[i] = a[i] + a[j]; a[j] = a[i] - a[j]; a[i] = a[i] - a[j]; } }