选择排序--堆排序

* 背景

       近期温习算法,看到自己尘封许久的算法专栏,也应该增加新的成员了。今天给大家分享的是堆排序,选择排序的一种算法。

       堆排序是利用对这种数据结构而设计的一种排序算法,他的最坏,最好,平均时间复杂度均为o(nlogn),同时也是不稳定的排序。


* 知识储备

       堆是具有如下性质的完全二叉树:每个节点的内容都大于或等于其左右孩子节点的值,为大顶堆;或者每个节点的值都小于等于其左右孩子节点的值成为小顶堆。如下图:(图片来自社会)

这里写图片描述

       对于堆的数据结构我们的存储方式可以采用线性存储,也可以采用链式存储,这里我们用线性存储的数组来存放他。
这里写图片描述

       如果用数组的存储,我们来描述一下他的堆的定义:
大顶堆:arr[i]>=arr[2*i+1] && arr[i]>=arr[2*i+1]
小顶堆:arr[i]<=arr[2*i+1] && arr[i]<=arr[2*i+1]


* 堆排序的步骤

       首先规定,我们排序的结果是升序,那我们就要构造大顶堆,这时根节点便是最大值,此时我们用最后一层的叶子节点与其交换,此时末尾的便是最大值,然后将剩余的n-1个节点重新构造成一个最大堆,然后根节点便成为n-1个节点的最大值,如此反复,便可以得到一个升序的序列了。

3.1 第一次建堆

       这里我们说一下建堆的过程,假定无序序列为:4,6,8,5,9,转换为二叉树的结构为:

这里写图片描述

       1. 我们从最后一层的叶子节点(从右往左)的父节点开始,使其满足最大堆的性质,那么我们先比较的是根节点为6的节点和其孩子节点是否满足性质。首先比较其左右孩子节点,5和9,发现9大,之后比较根节点6和右孩子9,发现9依然大,我们让9占到根节点的位置,那么6呢?因为9没有孩子节点了,所以6可以直接占到右孩子的位置。至此,这三个节点变满足了最大堆的性质了。构建过程如下:

这里写图片描述

       2.最后一层的叶子节点的父节点我们已经排完了,继续上一层叶子节点(从右到左)的父节点为4,我们构建最大堆,这是比较父节点为4的孩子节点9,8发现9大,于是我们比较根节点4和9,依然是9大,这时9便坐到根节点的位置,那么原来的4呢?直接和左孩子交换吗?不是的,我们可以存在临时变量tmp里,这是我们让左孩子成为父节点,比较其孩子节点,发现6大,这是我们比较tmp和6,发现6比4大,于是原来右孩子6现在成为父节点,而4最后坐到了右孩子的位置,至此我们构建成大顶堆。构建过程如下:

这里写图片描述
这里写图片描述
       


3.2 循环建堆

       将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。具体重建堆的步骤不再赘述,请参考3.1

这里写图片描述
这里写图片描述
这里写图片描述

       最后的调整结果为:

这里写图片描述


* 代码实现

       如果这里没有代码,我认为我便是在耍流氓。下面是自己写的代码,当然之前自己也手写过,发现还是有必要执行一下,我们的思想和机器的思想有一些差别的。

/**
 * 建堆过程(最小堆)-lyw--2018年8月25日23:40:52
 */
  public void sfit(int[] arr,int k,int n){
       int i=k;
       int j=2*i+1;
       int tmp=arr[k];

       while (j<=n){
           if (j<n && arr[j]>arr[j+1]){
               j++;
           }
           if (tmp <arr[j]){
               break;
           }else {
               arr[i]=arr[j];i=j;j=2*i+1;
           }
       }
       arr[i]=tmp;
    }
  /**
   * 交换
   * @param
   * @param 
   */
   public void swap(int[] arrTmp,int x,int y){
       int tmp=arrTmp[x];
       arrTmp[x]=arrTmp[y];
       arrTmp[y]=tmp;
   }
int[] arr={1,0,12,2,5,8,2,9,3};
/**
 * 堆排序全过程--刘雅雯--2018年8月26日08:10:23
  */
 @Test
 public void heapSort(){
     //构建一个最小堆
     for (int i=arr.length/2;i>=0;i--){
         sfit(arr,i,arr.length-1);
     }
     //循环一次构建一个最小堆
     for (int i=arr.length-1;i>0;i--){
         //swap(arr[0],arr[i]);
         swap(arr,0,i);
         sfit(arr,0,i-1);
     }

     //打印结果
     System.out.print("堆排序的结果:");
     for (int i=0;i<arr.length;i++){
         System.out.print(arr[i]+", ");
     }
  }

* 结语

       真的做起来也不是很困难,更多的是我们想不想话时间去做下去,我想这也是高级开发程序员必备的技能吧。

评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值