【排序算法】 堆排序 heap sort(选择类排序)

来源:互联网 发布:迪蒙小额贷款系统源码 编辑:程序博客网 时间:2024/06/05 07:25

【排序算法】 堆排序 heap sort(选择类排序)

堆排序的过程

创建最大堆:

从最后一个非叶子结点到根结点(从下向上,这点非常关键)调整每一个子堆,使子堆有序化。
最开始的子堆的根结点是最后一个非叶子结点,它有一个或者两个孩子,它与较大者交换位置即可实现子堆有序化。
底部的子堆有序化,是其上面的堆进行调整的前提条件。
也即是,如果子堆是无序的,那么包含它的一个更大的堆就无法进行调整操作,
所以需要从最后一个非叶子结点到根结点(从下向上,这点非常关键)调整每一个子堆,使子堆有序化。。

调整最大堆(有序化堆):

(只有一个或者零个父结点相对孩子是无序)
如果当前结点有孩子结点,
如果当前结点的孩子结点的较大者比当前结点大,则交换两者位置,
否则调整结束,退出,
继续比较当前结点与其新的孩子结点

堆排序的步骤:

  1. 初始化最大堆(使堆有序化,可能所有的子堆都是无序的,需要调整所有的子堆)
  2. 将最大堆的第一个元素和最后一个元素交换位置;
    使堆的长度减 1;
    最大堆调整(使堆有序化,只有第一个元素相对于它的孩子是无序的,不断调整此元素和其后代的相对位置即可);
    重复以上三步,直到堆的长度为 1。

索引关系

使用数组存储完全二叉树(堆),则结点下标关系是:

父结点 i 的左子结点是 2 * i + 1;
父结点 i 的右子结点是 2 * i + 2;
子结点 i 的父结点是 floor((i-1)/2)
后一个非叶子结点是 floor(N/2)-1

时间复杂度

expense:
时间复杂度 O(N*log(N))

应用场景

堆排序实现

package algorithm.algorithm4.ch02_sort;import org.junit.Test;import java.util.Arrays;import java.util.Random;/** * algorithm: heap sort,堆排序 */public class Heap {    public static void sort (Comparable[] arr){        if (arr == null || arr.length < 2) return;        // 1. 创建堆        // 从下向上使每一个有孩子的结点都成为一个有序堆(宏观角度)        // 多次堆化,从下到上不断新加元素,因为单次堆化要求最多只有根结点相对于孩子结点是无序的,所以需要从后往前添加元素        // 具体的堆化操作是从上到下完成的,而且一次堆化要求最多只有根结点相对于孩子结点是无序的,此外所有的子堆都是有序的        for (int i = arr.length/2-1; i >= 0; i --)  // 完全二叉树中最后一个有孩子的结点是 N/2-1            maxHeapify(arr, i, arr.length);        // 2. 堆排序        for (int j = arr.length - 1; j > 0; j --) {            swap(arr, 0, j);  // 堆首元素不断和堆尾元素交换位置,堆的大小减 1            maxHeapify(arr, 0, j);  // 将去掉一个元素的新堆调整为最大堆(有序堆)        }    }    /**     * 从上到下有序化堆,使小于孩子的父结点下沉(sink)     * @param arr 二叉堆     * @param index 待调整的子堆的根结点     * @param newSize 除了排序后的元素外,堆中剩余元素的个数     */    // 具体的堆化操作是从上到下完成的,而且一次堆化要求最多只有根结点相对于孩子结点是无序的,此外所有的子堆都是有序的    private static void maxHeapify (Comparable[] arr, int index, int newSize){        while (2 * index + 1 < newSize){  // 当前结点至少有左孩子            int childIdx = 2 * index + 1;  // index 左孩子的下标            // 存在右孩子,且右孩子比左孩子更大,则记录右孩子的下标            if (childIdx + 1 < newSize && arr[childIdx].compareTo(arr[childIdx+1]) < 0) childIdx ++;            if (arr[childIdx].compareTo(arr[index]) <= 0) break;  // 两个孩子均小于等于当前元素,则退出循环            swap(arr, index, childIdx);            index = childIdx;  // 当前不断跟新的孩子比较,跟其中一个较大的孩子交换位置        }    }    private static void swap(Comparable[] arr, int i, int j){        Comparable tmp = arr[i];        arr[i] = arr[j];        arr[j] = tmp;    }    @Test    public void test(){        Random random = new Random();        int N = 20;        Integer[] arr = new Integer[N];        for (int i = 0; i < N; i ++) arr[i] = random.nextInt(100);        System.out.println(Arrays.toString(arr));        Heap.sort(arr);        System.out.println(Arrays.toString(arr));    }}
原创粉丝点击