原地堆排序

原地堆排序

基于堆相关的操作,我们可以很容易的定义堆排序。例如,假设我们已经读入一系列数据并创建了一个堆,一个最直观的算法就是反复的调用extractMax函数,因为该函数总是能够返回堆中最大的值,然后把它从堆中删除,从而对这一系列返回值的输出就得到了该序列的降序排列。真正的原地堆排序使用了另外一个小技巧。

堆排序的过程
  1. 创建一个堆 H[0...n1] H [ 0... n − 1 ]
  2. 把堆首(最大值)和堆尾互换
  3. 把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置
  4. 重复步骤2,直到堆的尺寸为1
实现代码
package Heap.HeapSort;

/**
 * @ Description: 利用堆的shiftDown思想实现堆排序,不需要堆数据结构
 * @ Date: Created in 09:04 2018/7/31
 * @ Author: Anthony_Duan
 */
public class HeapSortInPlace {
    private HeapSortInPlace(){}

    public static void sort(Comparable[] arr) {

        int n =arr.length;


        /**
         * 因为是原地排序 所以索引从0开始
         * 从(最后一个元素索引-1)/2开始
         * 最后一个元素索引n-1
         *
         * 第一个for循环是将一个数组转化为堆
         * 第二个for循环是将堆中第一个元素(最大值)与最后一个元素进行交换
         * 此时最大值已经在属于它的位置上了,
         * 对数组第一个到倒数第二个的执行shiftDown操作 此时又是一个最大堆
         * 执行到最后所有元素都会按从小到大排列
         */
        for (int i = (n - 1 - 1) / 2; i >= 0; i--) {
            shiftDown2(arr, n, i);
        }

        for (int i = n - 1; i > 0; i--) {
            swap(arr, 0, i);
            shiftDown2(arr, i, 0);
        }
    }


    private static void swap(Object[] arr, int i, int j) {
        Object t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    /**
     * 原始shiftDown过程
     * @param arr
     * @param n
     * @param k
     */
    private static void shiftDown(Comparable[] arr, int n, int k) {
        while (2 * k + 1 < n) {
            int j = 2 * k + 1;
            if (j + 1 < n && arr[j + 1].compareTo(arr[j]) > 0) {
                j += 1;
            }
            if (arr[k].compareTo(arr[j]) >= 0) {
                break;
            }
            swap(arr, k, j);
            k = j;
        }
    }

    /**
     * 优化版shiftDown过程,使用赋值的方式取代不断的swap
     * 这个优化的思路与优化插入排序的思路一样
     * @param arr
     * @param n
     * @param k
     */
    private static void shiftDown2(Comparable[] arr, int n, int k) {
        Comparable e = arr[k];
        while (2 * k + 1 < n) {
            int j = 2 * k + 1;
            if (j + 1 < n && arr[j + 1].compareTo(arr[j]) > 0) {
                j += 1;
            }
            if (e.compareTo(arr[j]) >= 0) {
                break;
            }
            arr[k] = arr[j];
            k = j;
        }
        arr[k] = e;

    }
    public static void main(String[] args) {

        int N = 1000000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        SortTestHelper.testSort("Heap.HeapSort.HeapSortInPlace", arr);

        return;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值