Loading

排序算法之堆排序

堆排序

时间复杂度 O(nlogN)

大顶堆:

大顶堆是一个完全二叉树,其左右子节点小于或等于根节点,左右子节点之间并无关系

由此可知大顶堆的根节点为最大值

例:

int[] a = {3,4,2,7,1,6,5,0};

其构建的初始堆为

3 4 2 7 1 6 5 0

调整为大顶堆:

7 4 6 3 1 2 5 0 

了解了大顶堆后,再来看堆排序的步骤:

  • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  • 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

在上图中,就是将0与7调换,然后a[7] = 7;a[0] = 0;很显然此时不满足大顶堆性质,于是对a[0]~a[6]进行堆调整,调整后6为根节点,再将a[6]与a[0]交换,此时a[6]=6,a[7] = 7;可见是有序的,重复该步骤即可。

详细实现见源码

java实现:

public class HeapSort {
    public static void main(String[] args){
        int[] a = {3,4,2,7,1,6,5,0};
        heapSort(a);
    }

    /**
     * 堆大小
     */
    private  static int  len;

    /**
     * 堆排序
     * @param arr
     * @return
     */
    public static int[] heapSort(int[] arr) {
        // 构建大顶堆
        buildMaxHeap(arr);

        for (int i = arr.length - 1; i > 0; i--) {
            // 首尾元素交换,这样arr[length-1]就是最大值
            swap(arr, 0, i);
            // 减少下次堆调整的大小,忽略上一条语句交换得到的最大值,此时数组从后面逐渐变成有序的
            len--;
            // 因为根节点变动,所以需要重新调整
            heapify(arr, 0);
        }
        return arr;
    }

    /**
     * 给定一个数组构造大顶堆
     * @param arr
     */
    private static void buildMaxHeap(int[] arr) {   
        System.out.println("buildMaxHeap start");
        len = arr.length;
        for (int i = len/2; i >= 0; i--) {
            heapify(arr, i);
        }
        System.out.println("buildMaxHeap end");
    }

    /**
     * 堆调整 
     * @param arr 待调整的元素
     * @param i 调整第i位节点及其左右节点,根节点 i = 0
     */
    private static void heapify(int[] arr,int i) {     // 堆调整
        print(arr);
        int left = 2 * i + 1, // // i节点的左子结点
                right = 2 * i + 2, // i节点的右子结点
                largest = i; // 记录这三个节点最大的那个
        // 如果第i节点的左节点存在且左节点比它大,记录largest
        if (left < len && arr[left] > arr[largest]) {
            largest = left;
        }
        // 如果第i节点的右节点存在且比arr[largest]大,记录largest
        // 注意largest既可能为根节点i,也可能为i的左节点,总之这三个节点要保证堆的性质,找到这三个节点最大的那个置于根节点,记录largest
        if (right < len && arr[right] > arr[largest]) {
            largest = right;
        }


        if (largest != i) {
            // 如果largest != i,则说明节点i小于其左右节点,因此需要交换位置,保证根节点最大
            swap(arr, i, largest);
            // 因为此时堆发生了变动,所以需要重建交换的子节点,保证该节点保证堆性质
            heapify(arr, largest);
        }
    }

    /**
     * 数组交换
     * @param arr
     * @param i 数组下标
     * @param j 数组下标
     */
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

   
    /**
     * 数组输出
     * @param array
     */
    private static void print(int[] array){

        for(int a : array){
            System.out.print(a+" ");

        }
        System.out.print("\n");

    }
}

输出:

buildMaxHeap start
3 4 2 7 1 6 5 0 
3 4 2 7 1 6 5 0 
3 4 2 7 1 6 5 0 
3 4 6 7 1 2 5 0 
3 4 6 7 1 2 5 0 
3 7 6 4 1 2 5 0 
3 7 6 4 1 2 5 0 
7 3 6 4 1 2 5 0 
7 4 6 3 1 2 5 0 
buildMaxHeap end
0 4 6 3 1 2 5 7 
6 4 0 3 1 2 5 7 
6 4 5 3 1 2 0 7 
0 4 5 3 1 2 6 7 
5 4 0 3 1 2 6 7 
5 4 2 3 1 0 6 7 
0 4 2 3 1 5 6 7 
4 0 2 3 1 5 6 7 
4 3 2 0 1 5 6 7 
1 3 2 0 4 5 6 7 
3 1 2 0 4 5 6 7 
0 1 2 3 4 5 6 7 
2 1 0 3 4 5 6 7 
0 1 2 3 4 5 6 7 
1 0 2 3 4 5 6 7 
0 1 2 3 4 5 6 7 

Process finished with exit code 0
posted @ 2019-02-27 16:26  李帆1998  阅读(119)  评论(0编辑  收藏  举报