堆排序(headSort)

堆排序(headSort)

定义

按照惯例我们先来点入门的知识点,这样方便大家理解后面的知识点。
按照百科的说法,堆排序是指利用堆积树这种数据结构所设计的一种排序算法,但对于没有接触这个算法的人来说,会误以为是需要将数组转化为一种树型结构再进行排序,但其实并不是如此,堆排序只是利用树的性质,并利用数组的特点快速定位指定索引位置。
举个例子:
数组 【1,2,3,4,5,6】,长度为len=6
我们可以把它看作是一棵完全二叉树的存储结构

这里写图片描述
那么对于任何非叶子结点(1,2,3)来说,它们的左、右子结点为 i*2 , i*2+1 (如果存在);
最后一个非叶子结点就是len/2;
比如结点i=1的左孩子结点为 1*2=2, 右孩子结点为 1*2+1=3 。最后一个非叶子结点为 6/2=3;

  • 注意这里是下标从1开始,如果下标从0开始,即【0,1,2,3,4,5】,那么非叶子结点的左右子结点为 *i*2+1 , i*2+2 ;最后一个非叶子结点则为len/2-1;

堆排序

理解完上面这些,再来学习堆排序就相对简单多了。
我先讲一下堆排序思路:

1、构造最大堆(最小堆)
首先,构造最大堆(或最小堆),最大堆要求父结点不小于其左右子结点(最小堆就是父结点不大于其左右子结点),举个例子吧,看下图,图1是最大堆,图2是最小堆

这里写图片描述这里写图片描述
如何构造最大堆?从最后一个非叶子结点开始,从右往左,从下往上遍历结点,每一个结点都与其左右子结点相比较(如果存在),如果父结点的值小于其子结点的值 ,则将父结点与子结点的位置进行调换。遍历完之后,数组中的最大值就会被移动到堆顶。

2、把堆顶元素与最后一个元素互换位置,对前len-1个元素重新进行构造最大堆。

把堆顶元素与最后一个元素互换位置,对剩余前len-1个元素重新进行构造最大堆,重新此过程直到只剩下最后一个元素,这时候数组就已经排序完成。

举个粟子吧

数组【16,7,3,20,17,8】,len=6
先把它看成一棵完全二叉树
这里写图片描述
接下来我们进行构造最大堆,首先从最后一个非叶子结点(3)开始,3比8小,将3跟8交换位置
这里写图片描述 —–》这里写图片描述
然后我们再看倒数第二个非叶子结点(7),7 < 16 < 20 ,7跟20交换位置
这里写图片描述 —–》这里写图片描述
第2层的非叶子结点已经遍历完,接下来再往上找非叶子结点,看16,16 < 20 ,所以16跟20交换位置
这里写图片描述 ——》这里写图片描述
这样最大值已经移动到了堆顶,将堆顶元素与最后一个元素交换位置,重新构造前len-1位元素(即【3,16,8,7,17】,len=5)的最大堆,再与第len位元素交换位置。
这里写图片描述

例子是参考这位老哥的:https://www.cnblogs.com/0zcl/p/6737944.html

最后给上用java实现的代码

package 算法题;/**
 * Created by VLoye on 2018/7/26.
 */

/**
 * @author VLoye
 * @ClassName HeapSort
 * @Description
 * @Date 11:50  2018/7/26
 * @Version 1.0
 **/
public class HeapSort {

    private static void heapSort(int[] arr) {
        int len = arr.length ;
        //len/2-1 即最后一个有孩子的结点
       makeHeap(arr,len);
        len--;
        while (len >=0){
            swap(arr,0,len);    //将堆顶元素与尾节点交换后,长度减1,尾元素最大
            makeHeap(arr,len--);    //再次对堆进行调整
        }
    }
    //构造堆  自下往上
    public static void makeHeap(int[] arr,int len){
        for(int i = len/2 - 1; i >=0; i --){ //堆构造,自下往上
            heapAndjust(arr,i,len);
        }
    }
    //被makeHeap调用
    public static  void heapAndjust(int[] arr,int i,int len){
        int left,right,j ;
        while((left = 2*i+1) < len){    //判断当前父节点有无左节点(即有无孩子节点,left为左节点)
            if((right=left+1) >= len)
                right = left;
            j = left;   //j"指针指向左节点"
            if(j < len && arr[left] < arr[right])    //右节点大于左节点
                j ++;     //当前把"指针"指向右节点
            if(arr[i] < arr[j])    //将父节点与孩子节点交换(如果上面if为真,则arr[j]为右节点,如果为假arr[j]则为左节点)
                swap(arr,i,j);
            else         //说明比孩子节点都大,直接跳出循环语句
                break;
            i = j;
        }
    }

    public static  void swap(int[] arr,int i,int len){
        int temp = arr[i];
        arr[i] = arr[len];
        arr[len] = temp;
    }

    public static void main(String[] args) {
        int array[] = {20,50,20,40,70,10,80,30,60,90};
        int array2[] ={16,7,3,20,17,8};
        System.out.println("排序之前:");
        for(int element : array2){
            System.out.print(element+" ");
        }
        heapSort(array2);
        System.out.println("\n排序之后:");
        for(int element : array2){
            System.out.print(element+" ");
        }
    }
}

其实理解了也不是很难,就是 重复构造最大堆,交换堆顶跟最后一位元素的位置,直到堆只有一个元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值