堆排序
堆:如果一棵完全二叉树的每个节点都大于(小于)它的子节点称之为堆。
k >= 2k + 1 && k >= 2k + 2
或者 k <= 2k + 1 && k <= 2k + 2
堆分:大顶堆和小顶堆。
完全二叉树:除了叶子节点所有的其他节点都有完整的左子树和右子数,除了最后一层的非叶子节点以外。
使用堆排序分两步:
1 建立一个无序的堆。
2 输出对顶元素,然后用最后一个元素代替堆顶元素,是之再成为一个堆。
比如: 3,22,8,11,55,1这样一个数使用堆排序的过程如下:
1. 先按顺序建立一颗完全二叉树:
按层级顺序填充数据,从最后一个非叶子节点看,是否满足堆的要求。
先从8开始,不满足堆的要求,和1换位。(该节点都必须大于或者小于左右子节点),我们按小顶堆排序,所以节点都应小于左右子节点。
再从22开始,不满足要求,和11换位。(若该节点小于左右时,和较小的子节点互换)
再从3开始,不满足要求,和1互换位置。
这样就建成了堆。所有的节点都不大于它的子节点。
2 第二阶段。
从初始的堆中输出堆顶元素,然后将最后一个元素放到堆顶,然后在进行堆的定义置换。
我们把较大元素从顶上落到底层的过程叫做------- 筛选。
先输出1,然后将8置于堆顶
然后按照步骤1的方式将其筛选成符合条件的堆,知道8落地为止,即筛选8。
先和3替换,8落地。符合堆
输出3,将55放置堆顶
然后筛选55得到如下
输出8,,22置堆顶
筛选22,将11置堆顶,
输出11,输出22,输出55
排序后应该是1,3, 8,11, 22, 55
我们可以用一维数组表示这个完全二叉树。
假如当前节点为K:
那么父节点为: (k - 1) / 2
左孩子为: 2k + 1
有孩子为:2k + 2
其Java代码实现如下:
package andy.test; /** * @author Zhang,Tianyou * @version 2014年11月7日 下午10:32:31 */ public class HeapSortTest { public static void main(String[] args) { int[] a = {3,22,8,11,55,1}; heapSort(a); } private static void heapOne(int[] a, int n, int k) { //节点k进行筛选 //a: 堆数据 n:堆中有效数据个数 k:筛选节点 int k1 = 2*k + 1;//左子树序号 int k2 = 2*k + 2;//右子数序号 if(k1 >= n && k2 >= n)return ; //已经是叶子节点 int a1 = Integer.MAX_VALUE; int a2 = Integer.MAX_VALUE; if(k1 < n)a1 = a[k1]; //左孩子值 if(k2 < n)a2 = a[k2]; //右孩子值 if(a[k] <= a1 && a[k] <= a2)return; //已符合堆的要求 //找到左右孩子中最小的,和它交换 if(a1 < a2){ int temp = a[k]; a[k] = a[k1]; a[k1] = temp; heapOne(a, n, k1); //继续筛选子树 }else{ int temp = a[k]; a[k] = a[k2]; a[k2] = temp; heapOne(a, n, k2); //继续筛选子树 } } static void heapSort(int[] a){ //建立初始堆 //a.length 最后一个叶子节点 //a.length/ 2 最后一个非叶子节点 for(int i = a.length / 2; i >= 0; i--)heapOne(a, a.length, i); //边输出堆顶元素,边调整 int n = a.length;//剩余元素 while(n > 0){ System.out.print(a[0] + " ");//输出堆顶元素 a[0] = a[n - 1]; //最后一个元素置顶 n--; heapOne(a, n, 0);//筛选第一个元素 } System.out.println(); } }
输出结果为:
1 3 8 11 22 55