zoukankan      html  css  js  c++  java
  • 堆排序

    堆排序

    堆排序(Heap Sort)算法是基于选择排序思想的算法,其利用堆结构和二叉树的一些性质来完成数据的排序。

    什么是堆结构

    堆排序的关键是首先构造堆结构。那么什么是堆结构呢?堆结构是一种树结构,准确地说是一个完全二叉树。在这个树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件:

    ・如果按照从小到大的顺序排序,要求非叶结点的数据要大于或等于其左、右子结点的数据。

    ・如果按照从大到小的顺序排序,要求非叶结点的数据要小于或等于其左、右子结点的数据。

    堆排序过程

    一个完整的堆排序需要经过反复的两个步骤:构造堆结构和堆排序输出。下面首先分析如果构造堆结构。

    构造堆结构

    构造堆结构就是把原始的无序数据按前面堆结构的定义进行调整。首先,需要将原始的无序数据放置到一个完全二叉树的各个结点中,这可以按照前面介绍的方法来实现。

    然后,由完全二叉树的下层向上层逐层对父子结点的数据进行比较,使父结点的数据大于子结点的数据。这里需要使用“筛”运算进行结点数据的调整,直到所有结点最后满足堆结构的条件为止。筛运算主要针对非叶结点进行调整。

    例如,对于一个非叶结点Ai,假定Ai的左子树和右子树均已进行筛运算,也就是说其左子树和右子树均已构成堆结构。对Ai进行筛运算,操作步骤如下:

    (1)比较Ai的左子树和右子树的最大值,将最大值放在Aj中。

    (2)将Ai的数据与Aj的数据进行比较,如果Ai大于等于Aj,表示以Ai为根的子树已构成堆结构。便可以终止筛运算。

    (3)如果Ai小于Aj,则将Ai与Aj互换位置。

    (4)经过第(3)步后,可能会破坏以Ai为根的堆,因为此时Ai的值为原来的Aj。下面以Aj为根重复前面的步骤,直到满足堆结构的定义,即父结点数据大于子结点。这样,以Aj为根的子树就被调整为一个堆。

    在执行筛运算时,值较小的数据将逐层下移,这就是其称为“筛”运算的原因。

    堆排序

    (1)根据堆结构的特点,堆结构的根结点也就是最大值。这里按照从小到大排序,因此将其放到数组的最后。

    (2)对除最后一个结点外的其他结点重新执行前面介绍的构造堆过程。

    (3)重复上述过程,取此时堆结构的根结点(最大值)进行交换,放在数组的后面。

     

    动图演示

     

    示例:

    public class ClassP4_6 {
    
        static void constructHeap(int[] arr, int length_n) {
            int j, temp;
    
            // 构建堆结构
            for (int i = length_n / 2 - 1; i >= 0; i--) {//i为 非叶子节点
                while (2 * i + 1 < length_n) {//i节点有左子树
                    j = 2 * i + 1;//j为 左子树
                    if (j + 1 < length_n) {
                        if (arr[j] < arr[j + 1]) {//左子树 小于 右子树,则需要比较右子树
                            j++;//指向右子树
                        }
                    }
                    if (arr[i] < arr[j]) {
                        //交换 非叶子节点 和 左右树节点中的最大值
                        temp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = temp;
                        System.out.println("i:" + i + ",j:" + j + "交换:" + ArrayUtils.toString(arr));
                        i = j;//堆被破坏,重新调整
                    } else {
                        break;
                    }
                }
            }
        }
    
        static void heapSort(int[] arr) {
            int length = arr.length;
            int temp;
    
            constructHeap(arr, length);
            for (int i = length - 1; i > 0; i--) {
                System.out.println(i + ArrayUtils.toString(arr));
                // 交换 根节点(最大值) 和最后1个节点
                temp = arr[0];
                arr[0] = arr[i];
                arr[i] = temp;//每次都把最大值放到 剩下节点的最后1个节点
                constructHeap(arr, i);//对剩下的节点 构建堆结构
            }
            System.out.println(ArrayUtils.toString(arr));
        }
    
        public static void main(String[] args) {
            // constructHeap(new int[]{0, 1, 2, 3, 4, 5, 6, 7}, 8);
            heapSort(new int[]{0, 1, 2, 3, 4, 5, 6, 7});
        }
    
    }

    结果:

    i:3,j:7交换:{0,1,2,7,4,5,6,3}
    i:2,j:6交换:{0,1,6,7,4,5,2,3}
    i:1,j:3交换:{0,7,6,1,4,5,2,3}
    i:3,j:7交换:{0,7,6,3,4,5,2,1}
    i:0,j:1交换:{7,0,6,3,4,5,2,1}
    i:1,j:4交换:{7,4,6,3,0,5,2,1}
    7{7,4,6,3,0,5,2,1}
    i:0,j:2交换:{6,4,1,3,0,5,2,7}
    i:2,j:5交换:{6,4,5,3,0,1,2,7}
    6{6,4,5,3,0,1,2,7}
    i:0,j:2交换:{5,4,2,3,0,1,6,7}
    5{5,4,2,3,0,1,6,7}
    i:0,j:1交换:{4,1,2,3,0,5,6,7}
    i:1,j:3交换:{4,3,2,1,0,5,6,7}
    4{4,3,2,1,0,5,6,7}
    i:0,j:1交换:{3,0,2,1,4,5,6,7}
    i:1,j:3交换:{3,1,2,0,4,5,6,7}
    3{3,1,2,0,4,5,6,7}
    i:0,j:2交换:{2,1,0,3,4,5,6,7}
    2{2,1,0,3,4,5,6,7}
    i:0,j:1交换:{1,0,2,3,4,5,6,7}
    1{1,0,2,3,4,5,6,7}
    {0,1,2,3,4,5,6,7}
    

      

    参考:Java常用算法手册4.7

  • 相关阅读:
    gcd
    Kuglarz
    三分题解
    杜教筛
    第一组dp解题报告
    dp总结1
    cf-BitwiseXor
    6.6总结
    图论总结
    CF1309总结
  • 原文地址:https://www.cnblogs.com/ooo0/p/13322309.html
Copyright © 2011-2022 走看看