zoukankan      html  css  js  c++  java
  • 数据结构 堆排序

      堆排序是一种树形选择排序方法。在排序过程中,将L[1...n]看成是一棵完全二叉树的顺序存储结构,通过完全二叉树的双亲结点(parent)和孩子结点之间的内在关系,在当前无序区中选择最大(或最小)的元素。

      堆的定义:n个关键字序列L[1...n]称为堆,当且仅当该序列满足:

      1 L[i]<=L[2i]且L[i]<=L[2i+1]

      2 L[i]>=L[2i]且L[i]>=L[2i+1]

      (1<=i<=|_n/2_|,后者表示小于或等于n/2的最大整数)

      满足第一种情况的堆称为小顶堆,满足第二种情况的堆称为大顶堆。在小顶堆中,对于任意结点,它是作为根结点的子树中最小元素,根结点是最小元素。大顶堆与此相反,根结点是最大元素。

      构造初始堆:n个结点的完全二叉树,最后一个结点是第|_n/2_|个结点的孩子结点。对某个结点为根的子树进行筛选(对于大顶堆,如果根结点值小于孩子结点较大者,则交换),如果破坏了下一级的堆,则向后筛选,直到以该结点为根的子树构成堆。向前依次对各结点(|_n/2_|~1)重复上述过程。

      举例(自下向上逐步调整为大顶堆):

      

      

      

      Java代码:

    1 // 构造初始大顶堆
    2 private void buildMaxHeap(int[] arr, int length) {
    3     // 反复调整堆
    4     for (int i = length / 2; i >= 1; i--) {
    5         adjustDown(arr, i, length);
    6     }
    7

      

     1 // 向下调整堆
     2 private void adjustDown(int[] arr, int parent, int length) {
     3     // 暂存双亲结点值
     4     int temp = arr[parent]; 
     5 
     6     // 沿着较大的孩子结点向下筛选
     7     for (int child = parent << 1; child <= length; child <<= 1) {
     8         // 如果存在右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点的下标
     9         if (child + 1 <= length && arr[child + 1] > arr[child]) {
    10             child++;
    11         }
    12 
    13         // 如果双亲结点值大于孩子结点,则结束筛选
    14         if (temp >= arr[child]) {
    15             break;
    16         }
    17 
    18         // 把孩子结点的值赋给双亲结点
    19         arr[parent] = arr[child];
    20         // 把孩子结点的下标赋给双亲结点
    21         parent = child;
    22     }
    23     
    24     // 把双亲结点值放在最终位置上
    25     arr[parent] = temp;
    26 }

     

      排序堆:交换堆顶元素和堆底元素(堆的最后一个元素),原来的堆顶元素不再属于堆,从根结点开始向下调整,把剩余的元素重新建成堆。重复上述过程,直到堆中只剩一个元素。

      举例:

      

      Java代码:

     1 // 堆排序
     2 public void heapSort(int[] arr, int length) {
     3     // 构造初始大顶堆
     4     buildMaxHeap(arr, length);
     5     
     6     // length-1次趟排序堆
     7     for (int i = length; i > 1; i--) {
     8         // 交换堆顶元素和堆底元素
     9         int temp = arr[i];
    10         arr[i] = arr[1];
    11         arr[1] = temp;
    12 
    13         // 把剩余的i-1个元素调整成大顶堆
    14         adjustDown(arr, 1, i - 1);
    15         System.out.format("第%d趟: ", length - i);
    16         print(arr, 1, length);
    17     }
    18 }

      完整的Java代码:

     1 public class HeapSort {
     2     // 构造初始大顶堆
     3     private void buildMaxHeap(int[] arr, int length) {
     4         // 反复调整堆
     5         for (int i = length / 2; i >= 1; i--) {
     6             adjustDown(arr, i, length);
     7         }
     8     }
     9     
    10     // 向下调整堆
    11     private void adjustDown(int[] arr, int parent, int length) {
    12         // 暂存双亲结点值
    13         int temp = arr[parent]; 
    14 
    15         // 沿着较大的孩子结点向下筛选
    16         for (int child = parent << 1; child <= length; child <<= 1) {
    17             // 如果存在右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点的下标
    18             if (child + 1 <= length && arr[child + 1] > arr[child]) {
    19                 child++;
    20             }
    21 
    22             // 如果双亲结点值大于孩子结点,则结束筛选
    23             if (temp >= arr[child]) {
    24                 break;
    25             }
    26 
    27             // 把孩子结点的值赋给双亲结点
    28             arr[parent] = arr[child];
    29             // 把孩子结点的下标赋给双亲结点
    30             parent = child;
    31         }
    32         
    33         // 把双亲结点值放在最终位置上
    34         arr[parent] = temp;
    35     }
    36     
    37     // 堆排序
    38     public void heapSort(int[] arr, int length) {
    39         // 构造初始大顶堆
    40         buildMaxHeap(arr, length);
    41         
    42         // length-1次趟排序堆
    43         for (int i = length; i > 1; i--) {
    44             // 交换堆顶元素和堆底元素
    45             int temp = arr[i];
    46             arr[i] = arr[1];
    47             arr[1] = temp;
    48 
    49             // 把剩余的i-1个元素调整成大顶堆
    50             adjustDown(arr, 1, i - 1);
    51             System.out.format("第%d趟: ", length - i);
    52             print(arr, 1, length);
    53         }
    54     }
    55 
    56     // 打印
    57     public void print(int[] arr, int begin, int end) {
    58         for (int i = begin; i <= end; i++) {
    59             System.out.print(arr[i] + " ");
    60         }
    61         
    62         System.out.println();
    63     }
    64 
    65     public static void main(String[] args) {
    66         // 待排序数组
    67         int[] arr = {0, 57, 40, 38, 11, 13, 34, 48, 75, 6, 19, 9, 7};
    68 
    69         // 调用快速排序方法
    70         HeapSort heap = new HeapSort();
    71         System.out.print("排序前: ");
    72         int length = 12;
    73         heap.print(arr, 1, length);
    74         heap.heapSort(arr, length);
    75         System.out.print("排序后: ");
    76         heap.print(arr, 1, length);
    77     }
    78 }

      输出结果:

     1 排序前: 57 40 38 11 13 34 48 75 6 19 9 7 
     2 第0趟: 57 40 48 11 19 34 38 7 6 13 9 75 
     3 第1趟: 48 40 38 11 19 34 9 7 6 13 57 75 
     4 第2趟: 40 19 38 11 13 34 9 7 6 48 57 75 
     5 第3趟: 38 19 34 11 13 6 9 7 40 48 57 75 
     6 第4趟: 34 19 9 11 13 6 7 38 40 48 57 75 
     7 第5趟: 19 13 9 11 7 6 34 38 40 48 57 75 
     8 第6趟: 13 11 9 6 7 19 34 38 40 48 57 75 
     9 第7趟: 11 7 9 6 13 19 34 38 40 48 57 75 
    10 第8趟: 9 7 6 11 13 19 34 38 40 48 57 75 
    11 第9趟: 7 6 9 11 13 19 34 38 40 48 57 75 
    12 第10趟: 6 7 9 11 13 19 34 38 40 48 57 75 
    13 排序后: 6 7 9 11 13 19 34 38 40 48 57 75 

     

      堆排序性能分析:

      空间复杂度:仅使用了常数个辅助单元,所以空间复杂度是O(1)。

      时间复杂度:建堆时间复杂度是O(n),之后执行n-1次向下调整操作,每次调整的时间复杂度是O(h),所以在最好、最坏和平均情况下,堆排序的时间复杂度是O(nlog2n)。

      稳定性:筛选后可能把后面的相同元素调整到前面,所以堆排序不稳定。例如,表L={1,2,2},构造初始堆后L={2,1,2},最终排序结果是L={1,2,2},相同元素的相对次序发生了变化。

     

      参考资料

      《2017年数据结构联考复习指导》P296-298

      数据结构示例——堆排序过程

  • 相关阅读:
    JavaScript之DOM总结
    图书馆管理系统——模板建立
    JavaScript阶段总结(二)
    JavaScript阶段总结(一)
    机房重构反思之视图
    web服务代理
    若要在加载设计器前避免可能发生的数据丢失,必须纠正以下错误
    ”/”应用程序中的服务器错误
    无法显示您正在查找的页面,因为使用了无效的方法(http谓词)。
    hibernate树
  • 原文地址:https://www.cnblogs.com/WJQ2017/p/8446265.html
Copyright © 2011-2022 走看看