堆排序

来源:互联网 发布:vpn 绕过公司网络监控 编辑:程序博客网 时间:2024/05/21 10:17

堆排序是基于完全二叉树实现的,堆分为大顶堆和小顶堆,顾名思义,大顶堆是指每个非终端节点的值大于其孩子节点的值,这样的构造使得根节点的值为数组中的最大值。对小顶堆也是同样的构造,将数组中最小的数放置在根节点,每一个双亲节点的值都小于孩子节点的值。
具体算法如下:
1、大顶堆算法实现:

public class Heap {   public static void main(String[] args){       int[] s={50,10,90,30,70,40,80,60,20};       HeapSort(s);   }//HeapSort的目标是初建大顶堆,并且针对调整后的数组进行重新调整。private static void HeapSort(int[] s) {    int len=s.length/2;    for(int i=len-1;i>=0;i--){ //该步进行大顶堆的初始化        HeapAdjust(s,i,s.length);    }    for(int i=s.length-1;i>1;i--){   //从最后一个节点开始向前调整,直到最后一个节点被调整        swap(s,0,i);  //0所在的位置就是根节点,也就是数组中的最大值,与数组中最后一个值进行交换        //HeapAdjust(s,0,i-1); //对除数组中最后一个元素以外的剩余数组元素重新进行大顶堆调整        //HeapAdjust(s,0,i-1)从这个函数可以看出,(i-1)!=0,所以i肯定不能等于1,所以i>1位此次循环的结束条件        SmallHeapAdjust(s,0,i-1);    }}private static void swap(int[] s, int i, int i2) {    int a=s[i];    s[i]=s[i2];    s[i2]=a;}private static void HeapAdjust(int[] s, int k, int len) {    int temp=s[k];          //temp中存放双亲节点的值   k为当前双亲节点的下标    for(int j=2*k;j<len;j*=2){  //k是双亲节点,那么2k是其左孩子节点        if(j<(len-1)&&s[j]<s[j+1])    // j是k的左孩子,那么j+1就是k的右孩子            j++;        if(temp>=s[j])            break;        //若双亲节点大于孩子节点中较大的,则该子树已满足大顶堆的定义,直接退出循环        s[k]=s[j];        //否则的话,让双亲节点的值为孩子节点中最大的值        k=j;   //将和双亲节点进行交换的孩子节点的下标保存,用于下一次循环,或者退出循环后的赋值。        }    s[k]=temp;    for(int i=0;i<s.length;i++){           System.out.print(s[i]+" ");       }    System.out.println();   }}

排序过程及结果如下:

50 10 90 80 70 40 30 60 20 50 10 90 80 70 40 30 60 20 50 90 70 80 20 40 30 60 10 90 80 70 60 20 40 30 50 10 80 70 40 60 20 10 30 50 90 70 60 40 50 20 10 30 80 90 60 50 40 30 20 10 70 80 90 50 40 10 30 20 60 70 80 90 40 20 10 30 50 60 70 80 90 30 20 10 40 50 60 70 80 90 10 20 30 40 50 60 70 80 90

2、小顶堆算法是在大定堆的基础上进行了简单修改,具体修改部分如下:

public static void SmallHeapAdjust(int[] s,int k,int len){    int temp=s[k];    for(int j=2*k;j<len;j*=2){        if(j<(len-1)&&s[j]>s[j+1])//既然是构造小顶堆,那自然得找两个孩子里的较小的值            j++; //j指向较小的值        if(temp<=s[j]) //此处同理也是找双亲节点和孩子节点中的较小值保存到双亲节点中            break;        s[k]=s[j];        k=j;    }    s[k]=temp;    for(int i=0;i<s.length;i++){           System.out.print(s[i]+" ");       }    System.out.println();  }

调用此段代码的结果如下:

50 10 90 30 70 40 80 60 20 50 10 40 30 70 90 80 60 20 50 10 40 30 70 90 80 60 20 10 30 40 50 70 90 80 60 20 20 30 40 50 70 90 80 60 10 30 40 60 50 70 90 80 20 10 40 50 60 80 70 90 30 20 10 50 60 90 80 70 40 30 20 10 60 70 90 80 50 40 30 20 10 70 80 90 60 50 40 30 20 10 90 80 70 60 50 40 30 20 10 

从两段代码的运行情况来看,堆排序基本没有浪费每一步的排序,即没有原地踏步走的情况,说明堆排序是一种比较好的排序方式,但是考虑到堆排序的比较与交换是跳跃式的,因此堆排序是一种不稳定的排序方式。而且堆排序无论是在最好、最坏还是平均情况下,其时间复杂度都为O(nlogn),空间消耗只有swap()函数的空间使用,因此堆排序是一种很好的排序方法。

0 0