• 堆排序算法


    1.介绍:

    堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种,它的最坏,最好,平均时间复杂度均为O(nlogn)。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

    2.什么是堆:

    堆具有以下性质:

    • 它是完全二叉树:
    • 每个结点的值都大于或等于(小于或者等于)其左右孩子结点的值。

    对堆中的节点进行从上到下,从左到右进行编号,对应着数组中的每个元素的下标。以大顶堆为例,堆中的逻辑结构对应着数组中的存储结构如下所示:

    所以对于堆中的父子节点之间对应关系为:

    大顶堆:arr[ i ] >= arr[ 2 * i + 1] 并且 arr[ i ] >= arr[ 2 * i + 2] ,其中  0 <= i <= arr.length - 1

    小顶堆:arr[ i ] <= arr[ 2 * i + 1] 并且 arr[ i ] <= arr[ 2 * i + 2] ,其中  0 <= i <= arr.length - 1

    其中,最后一个非叶子节点的编号(下标)为:arr.length / 2 - 1

     

    3.堆排序基本思想:

    这里我们以大顶堆来举例,小顶堆的思想类似。一共分为3步:

    1. 将待排序数组创建(调整)成一个大顶堆,此时,堆顶元素是数组的最大值。

    2. 将堆顶元素与末尾元素进行交换,此时末尾元素为最大值。

    3. 将剩余的元素[0.....n-1]重新调整成一个大顶堆,现在问题就转换为了对这n-1个元素进行堆排序了。反复执行以上步骤,直到全部元素都有序。

    下面我们以大顶堆为例,来演示堆排序的过程:


    创建堆:

    我们的目的是使得堆顶为最大的元素。首先,叶子节点满足堆的性质,我们不需要调整。从最后一个非叶子节点开始,它的下标为:arr.length / 2 - 1,即上图中的下标为2,元素值为1的节点,我们令当前调整的元素为节点i,让当前的节点与左右孩子节点进行比较,取左右孩子节点的最大值。(1)如果孩子节点的最大值不比节点i大,那么就不交换,继续调整节点i-1;(2)如果孩子节点的最大值比当前节点要大,那么就交换。交换以后,被交换的孩子节点可能不满足堆的性质,所以我们需要继续对孩子节点进行同样的操作,直到节点i对应的分支全部满足堆结构。节点i开始,每次i = i - 1 ,一直到堆顶节点。这样当所有的非叶子节点都调整为满足堆结构以后,堆顶元素就是最大的。

    步骤1:

    步骤2:


    步骤3:


    步骤4:

    由于步骤3中12和8的交换导致孩子节点(节点值为8)分支不满足堆结构,所以需要调整孩子节点。


    至此:大顶堆就创建好了,堆顶元素为最大的,接下来把堆顶元素和末尾元素进行交换。

    首尾交换:


    此时尾部元素12已经有序,并且为最大的,可以把它砍掉,我们继续对剩下的n-1个元素进行同样的操作就可以了。把1放在堆顶后,目前的结构不满足堆的性质,需要对堆顶元素按照上面的步骤进行调整,直到数组中的所有元素有序为止。

    4.代码演示(Java版):

    import java.util.Arrays;
    public class MaxHeap {
        public static void main(String[] args) {
            int[] arr = new int[]{8,6,1,12,10,7};
            sortHeap(arr);
            System.out.println(Arrays.toString(arr));
        }
        
        /**
         * 堆排序
         * @param arr
         */
        public static void sortHeap(int[] arr){
            //构建最大堆
            for(int i = arr.length / 2 - 1; i >= 0; i--){
                adjustHeap(arr, i, arr.length);
            }
            //首位交换,并重新调整堆结构
            for(int j = arr.length - 1; j >= 0; j--){
                //交换
                int temp = arr[j];
                arr[j] = arr[0];
                arr[0] = temp;
                //继续调整
                adjustHeap(arr, 0, j);
            }
        }
        
        /**
         * 调整最大堆
         * @param arr 原数组
         * @param i   当前需要调整的节点的编号 
         * @param length 剩余节点的个数
         */
        public static void adjustHeap(int[] arr,int i,int length){
            int temp = arr[i];//当前节点的值
            for(int k = 2 * i + 1; k < length; k = 2 * k + 1){
                //获取当前节点的孩子节点的最大值
                if(k + 1 < length && arr[k + 1] > arr[k]){
                    k = k + 1;
                }
                //如果孩子节点更大,则把最大的孩子节点的值赋值给父节点
                if(arr[k] > temp){
                    arr[i] = arr[k];
                    i = k;
                }
                else break; //说明当前节点往下都不需要调整了
            }
            arr[i] = temp;
        }
    }
  • 相关阅读:
    什么是 go vendor
    Golang包管理工具之govendor的使用
    国内的go get问题的解决
    集群、限流、缓存 BAT 大厂无非也就是这么做
    Gin框架中文文档
    GO——beego简单开发实例(二)
    C++11 并发指南四(<future> 详解一 std::promise 介绍)(转)
    C++11 并发指南三(std::mutex 详解)(转)
    C++11 并发指南二(std::thread 详解)(转)
    用C++设计一个不能被继承的类(转)
  • 原文地址:https://www.cnblogs.com/neuzk/p/9476419.html
Copyright © 2020-2023  润新知