堆排序

一、堆排序的介绍

  1. 堆排序是利用这种数据结构而进行设计的一种排序算法,堆排序是一种选择排序。它最好、最坏、平均的时间复杂度均为O(nlogn),并且是不稳定的。
  2. 堆是具有如下性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。注意:节点的左孩子和右孩子的值的大小关系没有要求。
  3.            
  4. 大顶堆$arr[i] >= arr[2*i+1]并且arr[i] >= arr[2*i+2]$;小顶堆$arr[i] <= arr[2*i+1]并且arr[i] <= arr[2*i+2]$
  5. 一般要求结果升序的序列采用大顶堆;而降序的序列采用小顶堆。

二、堆排序的思想

  1.  将待排序的序列构成一个大顶堆;
  2. 此时,整个序列的最大值就是堆顶的根节点;
  3. 将其与末尾元素进行交换,此时末尾就是最大值;
  4. 然后将剩余n-1个元素重新构成一个堆,同样的方法得到这n个元素的次小值。如此反复执行,便能得到一个有序序列。

三、代码

import java.util.Arrays;

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

    public static void heapSort(int[] arr) {
        System.out.println("堆排序");

        //build heap,共有(n/2-1)非叶子节点,从下往上,从左往右建立大根堆
        for (int i = arr.length/2 -1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        //将堆顶元素与莫为元素交换,最大值“沉”到数组末端
        int cnt = 0;
        for (int j = arr.length - 1; j > 0; j--) {
            swap(arr, j, 0);
            adjustHeap(arr, 0, j);
        }
        System.out.println("数组 = " + Arrays.toString(arr));
    }

    /**升序排列,构建大根堆
     * @param arr:  待调整的数组
     * @param i:    非叶子节点在数组中的索引
     * @param length:   表示待调整的元素的数量
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];
        //k=2*i+1是第i个节点的左子节点;k+1是第i个节点的右子节点
        for (int k = 2 * i + 1; k < length; k = k * 2 +1) {
            if (k+1 < length && arr[k] < arr[k+1]) {
                k += 1;
            }
            if (arr[k] > temp) {    //如果子节点k大于父节点i
                arr[i] = arr[k];
                i = k;
            } else {    //如果当前节点i已经最大,则不用交换(注意,由于是从左到右、从下到上调整大根堆)
                break;
            }
        }
        //当for循环结束,我们将以i为父节点的最大值,放在了最顶端(局部大顶堆)
        arr[i] = temp;  //将temp放到最终的位置
    }

    //交换函数
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}
堆排序
数组 = [1, 2, 3, 4, 5, 6]

 

四、关于堆排序的几点说明

1、堆排序是基于完全二叉树实现的,在将一个数组调整成一个(小根堆、大根堆)堆的时候,关键之一的是确定最后一个非叶子节点的序号。而完全二叉树的最后一个非叶子节点的序号是n/2-1,然后从左到右,从下至上,对二叉树进行调整。

2、排序的稳定性指的是,对于两个关键字相等的记录,他们在序列中的相对位置,在排序前后没有发生改变。排序算法是稳定的,那么第一个排序结果可以为另一个排序继续使用,即稳定的排序算法可以用于两个关键字的排序。比如,现在要根据英语成绩排一下大家的成绩,然后在根据数学成绩排一下大家的成绩,如果用的是稳定排序的话,就可以保证数学成绩相同的人里英语分高的人排在更前面。

3、堆排序是不稳定的:考虑序列{9,5A,7,5B},按照堆排序的算法走一遍(算法导论中用的是最大堆,这个序列也是用最大堆来设计的),输出序列为{5B,5A,7,9},而且与等号无关,因此堆排序是不稳定的。他之所以不稳定,因为在堆调整的过程中,在堆顶与堆尾交换的时候,两个相等的记录在序列中的相对位置可能发生变化,这就影响了排序算法的稳定性。

posted @ 2021-01-26 18:22  Peterxiazhen  阅读(131)  评论(0编辑  收藏  举报