堆排序是建立在堆的基础上的, 了解堆排序我们得先了解二叉堆. 二叉堆是以二叉树为基础的, 当一棵二叉树的每个结点都大于等于它的两个子节点数时, 它被称为堆有序. 我们可以很容易的理解出, 它的根节点是最大节点
二叉堆可以用指针和数组两种方式表示, 本文用的是数组的方式.
我们在堆中有两种操作方法, 一种叫做上浮, 一种叫做下潜, 上浮是指将节点n和它的父节点进项比较, 如果它的父节点小于它, 则节点n与它的父节点交换, 然后在继续与它交换后的父节点进行比较, 知道它的父节点不小于它为止. 而下潜则相反, 节点n 将于它的两个子节点比较, 当它的子节点大于它时, 它将于较大的那个子节点进行交换, 以此类推, 因为本文重点讲的是堆排序, 只用到了下潜, 所以只给出下潜代码:
1 public static void sink(int[] a, int i, int N){ // 将小的数字下沉 2 while(i*2+1 < N) { 3 int j = i*2+1; 4 if(j < N-1 && a[j] < a[j+1]) { 5 j++; 6 } 7 if(a[i] > a[j]) 8 break; 9 exch(a, i, j); 10 i = j; 11 } 12 }
然后我们讲一下堆排序. 堆排序分为两个步骤, 一个是将代码进行堆有序化, 从第n/2个结点到第一个结点分别进行下潜操作, 接着进行下沉排序, 将根节点与最后一个结点进行交换, 在将排除了最后一个数的数组对根节点作下潜操作, 然后将根节点和第n-1个结点交换, 以此类对, 知道到了最后一个结点
附上代码:
1 public static void hashSort(int[] a, int N) { 2 for(int i = N/2; i >= 0; i--) { // 实现堆有序 3 sink(a, i, N); 4 } 5 for(int i = N-1; i >= 0; i--) { // 排序 6 exch(a, 0, i); 7 sink(a, 0, i); 8 } 9 }
我的代码和书上的有些不同, 因为我的代码是从0开始的, 而书上的是从1开始的, 这只是我的个人习惯, 按照书上说的, 从一开始更加符合大多数程序员的习惯, 也更加方便
堆排序的时间复杂度为O(nlogn)