堆排序

排序

概念

  • 1.1 排序
    排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
    平时的上下文中,如果提到排序,通常指的是排升序(非降序)。
    通常意义上的排序,都是指的原地排序(in place sort)。

  • 1.2 稳定性
    两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序
    算法。

    9 , 5 , 2 , 7 , 12 , 2 , 4 , 6 , 9 , 23
    排序后
    前面的 2 依旧在后面的 2 的前面那么称这个算法具有稳定性

堆排序

其基本思想其实就是选择排序,只是寻求最大值的方式不同。选择排序是通过遍历求最大值(最小值),而堆排序是通过大顶堆和小顶堆的特性寻求最大值的,不需要数据移动,效率更高效一些。抽象起来是对堆的操作,也就是完全二叉树,所以时间复杂度为O(nlogn)。
我们可以用顺序存储数组表示堆。其中的关系为:当前元素下标的左孩子=当前元素下标 * 2 + 1,那么右孩子就是左孩子 + 1。
堆首元素就是0号下标对应的元素。所以只需要交换0号下标的元素到末尾即可。

注意的地方:交换完元素后,堆的特性可能就被破环了,所以当交换完成后一定要对改变后的堆进行调整(这里我才用的是向下调整)

所以我们的第一步就是建堆(建堆需要用到堆的调整方法见下文):

package 排序;

public class 堆排序 {
    /**
     * 调整大堆的方法,自顶向下调整
     * @param i       表示要调整的局部堆的根节点的下标表示
     * @param arr     待调整的数组(即堆)
     * @param length  length表示数组的长度(堆的元素个数)
     */

    //注意:该方法是调整堆,前提arr数组是堆(只要i位置的元素不满足堆的性质),
    //我们可以通过该方法调整i下标的元素的位置,让堆满足堆的定义
    public static void adjustHeap(int i,int[] arr,int length){
        //k则是i的左孩子的下标,k = i*2+1表示下一步的调整从k的左孩子开始
        for(int k = i * 2 + 1;k < length;k = i * 2 +1){
            if(k+1 < length && arr[k] < arr[k+1]){
                //先将k移动到左右孩子中大的一方
                k = k + 1;
            }
            if(arr[i] < arr[k]){
                //之后再比较父子节点的大小
                arr[i] = arr[i] ^ arr[k];
                arr[k] = arr[i] ^ arr[k];
                arr[i] = arr[i] ^ arr[k];
                //这里有一个回溯,就是当上层的节点将小的数换下来后,会重新调整下面的子堆
                i = k;  //将父节点的下标移动到子节点,因为经过交换后可能子节点的堆不满足堆的给定义了
            }else{
                //说明调整完毕了
                break;
            }
        }
    }

    /**
     * 堆排序
     * 思路:1.将数组调整为大顶堆,然后将最大的元素放到数组的最后
     *      2.将数组前length-1数再调整为大顶堆,将次大的数放在数组的倒数第二个位置
     *      3.重复类似的操作
     * @param arr   待排序的数组
     */
    public static void heapSort(int[] arr){
        //1.首先我们将arr调整为大顶堆,自底向上
        for (int i = arr.length / 2 -1; i >= 0 ; i--) {
            adjustHeap(i,arr,arr.length);
        }

        for (int i : arr) {
            System.out.println(i+" ");
        }
        //i是按堆的结构来说,从底向上的第一个非叶子节点
        for(int i = arr.length -1;i > 0;i--){
            //2.交换到后面
            arr[0] = arr[0] ^ arr[i];
            arr[i] = arr[0] ^ arr[i];
            arr[0] = arr[0] ^ arr[i];
            //将交换上去的小值调整下来
            adjustHeap(0,arr,i);
        }
    }
    public static void main(String[] args) {
        int[] arr = {9,7,6,4,2,45,75,23,21};
        heapSort(arr);
//        adjustHeap(2,arr,arr.length);
        for (int i : arr) {
            System.out.println(i+" ");
        }
    }
}

总结:
堆排序是不稳定的,在堆的调整过程中非常容易将元素的相对位置改变。
注意:排升序建大堆,排降序建小堆(都是与最后的元素交换)
大堆将大的元素交换到最后最后形成的数组就是升序!
反之,小堆将小的元素交换到后面,最后形成的数组就是降序的!

注:如有运行错误,请评论一下我尽快更改,谢谢啦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值