当前位置:   article > 正文

数据结构笔记(三)—— 堆排序详解_小根堆排序的详细步骤

小根堆排序的详细步骤

堆排序

1. 堆的概念

堆是具有以下性质的完全二叉树:

  1. 每个节点都大于或等于其做孩子节点的值,成为大顶堆。
  2. 每个节点的值都小于或等于左右孩子节点的值,称为小顶堆。

img

举例来说,对于n个元素的序列{R0, R1, … , Rn}当且仅当满足下列关系之一时,称之为堆:

(1) Ri <= R2i+1 Ri <= R2i+2 (小根堆)

(2) Ri >= R2i+1 且 Ri >= R2i+2 (大根堆)

2. 堆排序基本思想

1)首先,将数组R[0…n]调整为大顶堆或小顶堆(大顶堆排序后是从小到大,小顶堆排序后是从大到小,本文以大顶堆例)

2)调整结束后R0就是数组中最大的一个值,然后将R[0]和R[n]交换,输出R[n]。

3)因为最大值已经出来,我们需要将其排除再外,放到数组最后,然后继续找最大值。即将R[0…n-1]重新调整为堆,交换R[0]和R[n-1];

4)如此反复,直到交换了R[0]与R[1]为止。 此时的数组R[0…n]已经是顺序排序。

其实主要操作就两个:

  1. 根据初始数组去构造初始堆
  2. 每次交换第一个和最后一个元素后,输出最后一个元素,然后把剩下的元素重新调整为大顶堆

当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。

3. 步骤详解

步骤一 、构造初始堆。将给数组构造成一个大顶堆

  1. 假设给定无序序列结构如下

img****

  1. 此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。(发现右子节点比自己大,进行交换)

img

  1. 这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

img

  1. 此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。即可得到最终排序后的序列

将堆顶元素9和末尾元素4进行交换

img

重新调整结构,使其继续满足堆定义

img

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

img

4. 代码实现

package tree;

import java.util.Arrays;

/**
* @author lixiangxiang
* @description /
* @date 2021/6/22 10:58
*/
public class HeapSort {
   public static void main(String[] args) {
       int arr[] = {4, 6, 8, 5, 9};
       heapSort(arr);
       System.out.println(Arrays.toString(arr));
   }
   private static void heapSort(int[] arr) {
       //将数组构建成大顶堆
       for(int i = arr.length / 2 -1; i >=0; i--) {
           adjustHeap(arr, i, arr.length);
       }
       //将堆顶元素和末尾元素叫魂
       //重新调整结构,使其满足堆定义
       int temp = 0;
       for(int j = arr.length-1;j >0; j--) {
           //交换
           temp = arr[j];
           arr[j] = arr[0];
           arr[0] = temp;
           adjustHeap(arr, 0, j);
       }
   }

  /**
   * description: 调整大顶堆的过程,
   *
   * @author: lixiangxiang
   * @param arr 数组
   * @param i 非叶子节点下标
   * @param length 数组的长度
   * @return void
   * @date 2021/7/11 11:40
   */
   private static void adjustHeap(int[] arr,int i,int length) {
       //取出当前元素
       int temp= arr[i];
       //获取当前元素的左子节点下表
       int child = 2 * i + 1;
       while(child < length ) {
           //如果右子节点的值大于左子节点,让child = 右子节点的下标 即获取孩子中值最大的一个的下标
           if (child + 1 < length && arr[child] < arr[child + 1]) {
               child ++;
           }
           //如果父节点的值已经大于等于子节点的值,则直接跳过
           if (arr[child] <= arr [i]) {
               break;
           }
           //否则将子节点的值给父节点
           arr[i] = arr[child];

           //以孩子为父节点,继续往下筛选
           i = child ;
           child = 2 * child + 1;
       }
       arr[i] = temp;
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/692662
推荐阅读
相关标签
  

闽ICP备14008679号