堆排序算法原理以及实例代码
1、 堆排序定义
n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
(1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤ )
若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示。
2、大根堆和小根堆
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆。
注意:
①堆中任一子树亦是堆。
②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。
3、堆排序特点
堆排序(HeapSort)是一树形选择排序。
堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系【参见二叉树的顺序存储结构】,在当前无序区中选择关键字最大(或最小)的记录。
4、堆排序与直接插入排序的区别
直 接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比 较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行 了这些比较操作。
堆排序可通过树形结构保存部分比较结果,可减少比较次数。
5、堆排序
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
?
完整的实现代码
?
public class Heap {
??? public static void main(String[] args) {
??? ?
??????? int[] a = { 26, 5, 77, 1, 33, 11, 34, 95, 48 };
??????? Sort(a);
??? }
???
??? public static void Sort(int[] a){
??????? int temp;
??????? int n = a.length;
??????? Display(a);
???????
??????? for(int i = n/2-1;i>=0;i--)??????????????????????? //从非叶子节点开始
??????????? Adjust(a, i, n);??????????????????????????? //初始化堆
??????? for(int i = n-1;i > 0;i--){
??????????? temp = a[0];??????????????????????????????? //交换根节点与最后一个叶子节点,要调整的范围减1
??????????? a[0] = a[i];
??????????? a[i] = temp;
??????????? Adjust(a, 0, i);????????????????????????? //不断减小长度、 交换、调整
??????? }???
??????? Display(a);
??? }
???
??? public static void Adjust(int[] a, int i, int n){???? //调整函数,参数含义 代表从哪个节点开始跳着,n代表堆的长度范围
??????? int j = 2*i+1;??????????????????????????????????? //从要调整的节点的子节点开始
??????? int temp = a[i];??????????????????????????????? // temp 记录要调整的节点
??????? while(j<n){??????????????????????????????????????? //还有叶子节点怎循环
??????????? if(j<n-1 && a[j]<a[j+1])??????????????????? //保证不越界,选择两子重的较大者
??????????????? j++;
??????????? if(temp >= a[j])??????????????????????????? //根节点较大,停止调整
??????????????? break;
??????????? a[(j-1)/2] = a[j];??????????????????????????? //子节点较大,本应该交换,但覆盖即可,temp保存着交换后该节点的信息
??????????? j = j*2+1;??????????????????????????????????? //继续检查子节点是否需要交互,如果越界则表明没有子节点
??????? }
??????? a[(j-1)/2] = temp;??????????????????????????????? //最后停下来的位置上赋上原始节点的值
??? }
???
??? public static void Display(int[] a){???????????????? //方便展示所以定义此函数
??????? System.out.println("------------------------");
??????? for(int i=0; i<a.length;i++){
??????????? System.out.print(a[i]+" ");
??????? }
??????? System.out.println();
??? }
}
?
?
?
?