排序算法--堆排序

算法简介:

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为(nlogn),它也是不稳定排序,首先了解一下

堆是具有以下性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。如图:

1024555-20161217182750011-675658660.png

同时,我们对堆中的节点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子


1024555-20161217182857323-2092264199.png

接下来,我们来看看堆排序的基本思想及实现步骤:

堆排序的基本思想及实现步骤

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾元素成为最大值。然后将剩余n-1个元素重新构造成一个大顶堆,这样会得到n个元素的次小值,此时根节点又会成为次小值中的最大值,再重复以上步骤,最终便会得到一个有序序列了。

步骤一 构造初始大顶堆(升序用大顶堆,降序小顶堆)

上图:
1.假定给定无序序列结构如下


1024555-20161217192038651-934327647.png

2.此时我们从最后一个非叶子节点(6)开始,从左至右,从下至上进行调整


1024555-20161217192209433-270379236.png

3.找到第二个非叶子节点4,由于(4,9,8)中9最大,4和9进行交换。
1024555-20161217192854636-1823585260.png

这时,交换导致了子根(4,5,6)结构的混乱,继续调整,(4,5,6)中6最大。交换4和6.
1024555-20161217193347886-1142194411.png

此时,我们就将一个无序序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆(此时调整的堆元素长度为n-1,与大顶堆根元素交换过的位置在下一次调整排除)然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

上图:
1.将堆顶元素9和末尾元素4交换:


1024555-20161217194207620-1455153342.png

2.重新调整结构,使剩下的n-1个元素继续成为大顶堆

1024555-20161218153110495-1280388728.png

3.再将堆顶元素8和末尾元素5进行交换,得到第二大元素8


1024555-20161218152929339-1114983222.png

后续过程,进行重复以上步骤,最终使得整个序列有序


1024555-20161218152348229-935654830.png

对堆排序的基本思路的简单总结:
a.将无序序列构建成一个堆,根据升序降序需求选择大顶堆还是小顶堆;

b.将堆顶元素与末尾元素进行交换,将最大(最小)元素“沉”到数组末端;

c.重新调整剩余的n-1(n-2,n-3....1)个结构,使其满足堆定义,然后继续交换堆顶元素和当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

代码实现:

public class Duition{
  public static void sort(int[] arr){
     int len = arr.length - 1;
    for(int i = len/2-1;i>=0;i--){
       //构造大顶堆
       headAdjust(arr,i,len);
     }
    //交换根元素后再进行堆调整
   while(len>=0){
       swap(arr,0,len--);
       headAdjust(arr,0,len);
    }
  }

 //交换指定元素
  public static void swap(int[] arr,int i,int j){
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }



//构造大顶堆
public static void headAdjust(int[] arr,int i,int len){
  int left; //左孩子
  int right;  //右孩子
  int j;
  
  while((left = i*2+1)<=len){
      right = left +1;
      j = left;
    
    //比较左右孩子,将其中较大者的位置赋予j
    if(j

其中 len/2-1是表示数组元素按曾编号之后非叶子节点的下标范围,在len/2-1之后的元素都是叶子节点(即没有左右孩子),这是完全二叉树的规律。

注:以上算法思想及图源自图解排序算法(三)之堆排序,代码已测试,没发现什么问题,做个记录,以便日后查询。

你可能感兴趣的:(排序算法--堆排序)