八大内部排序算法-堆排序

目录

一、堆的相关概念

二、堆排序动图

三、堆排序思路

 1、 建立逻辑上的大根堆

 2、堆顶元素和末尾元素进行交换,得到最大值下沉到数组末端,不断调整重建大根堆,再进行交换调整...


一、堆的相关概念

       堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。堆排序是利用堆这种数据结构而设计的一种排序算法,下面着重介绍一下堆的相关概念。直接借鉴百度百科。

      所以堆首先是一颗完全二叉树。

      每个结点的值都大于或等于其左右孩子结点的值,称为大根堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。

      每个结点的值都小于或等于其左右孩子结点的值,称为小根堆。

     

大根堆和小根堆
大根堆和小根堆

 


二、堆排序动图

堆排序动图
堆排序动图

三、堆排序思路

  1. 将无序序列构建成一个堆,根据升序降序需求选择大根堆或小根堆。

  2. 将堆顶元素和末尾元素交换,将最大元素“沉”到数组末端。

  3. 重新调整结构,使其满足堆定义,然后继续交换堆顶元素和当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

          说实话,找了好几篇帖子才看懂一点,另外我觉得堆排序似乎是“选泡插,快归堆希桶计基; 恩方恩老恩一三,对恩加K恩乘K。 选快堆希都不稳(ps:时常背口诀haha)中最复杂的,下面的思路都是建立的大根堆。

         以数组[3,7,8,20,16,17]为例子,众所周知,数组和二叉树是可以相互转化的,堆是作为二叉树的一种典型数据结构。

         首先明确很重要的一点就是整个排序过程中,并没有真正创建过树,整个过程中,其实都是在对数组进行操作。在对数组操作的时候,是以大根堆其中的规则来进行操作的。

        一般升序采用大根堆,降序采用小根堆

 1、 建立逻辑上的大根堆

         将数组调整成大根堆的时候是从最后一个非叶子结点开始(从下往上,从右往左)到最后一个非叶子结点结束为止。

 2、堆顶元素和末尾元素进行交换,得到最大值下沉到数组末端,不断调整重建大根堆,再进行交换调整...

         因为堆顶元素和末尾元素交换动作,往小了说,只动了两个元素的值,堆顶和末尾,而末尾元素我们不用去理会,就相当于在堆中只变动了堆顶元素,所以会造成每次调整重建大根堆,是从堆顶开始。(从上往下)

3、代码实现(ps:比较复杂)

代码的核心部分还是 :调整索引为i的非叶子结点下所有的子树,最后构成一个堆的结构

package com.zengwen.sort;

import java.util.Arrays;

public class HeapSort {

    public static void main(String[] args) {
        int arr[] = {3, 7, 8, 20, 16, 17};
        heapSort(arr);
    }

    public static void heapSort(int arr[]) {

        //A步骤一、建立逻辑上的大根堆(从最后一个非叶子结点(从下往上,从右往左))
        //i表示每个非叶子结点的索引值
        //在一次循环中,如果非叶子结点所在的子树,不满足大根堆的要求,则非叶子结点会和其左或右孩子结点进行交换
        for (int i = arr.length / 2 - 1; i >= 0 ; i--) {
            buildMaxHeap(arr,i,arr.length);
        }
        //B步骤二、堆顶元素和末尾元素进行交换,得到最大值下沉到数组末端,不断调整重建大根堆,再进行交换调整...
        for (int i = arr.length - 1; i > 0 ; i--) {
            //将堆顶元素和末尾元素交换
            int temp = arr[i];
            arr[i]  = arr[0];
            arr[0] = temp;
            //调整重建大根堆,从堆顶(从上往下))
            buildMaxHeap(arr,0,i);
        }
        System.out.println(Arrays.toString(arr));
    }
    
    //调整索引为i的非叶子结点下所有的子树 构成一个堆的结构
    /**
     * @param arr    待调整的数组
     * @param i      表示非叶子结点在数组中的索引
     * @param length 表示对多少个元素进行调整
     */
    public static void buildMaxHeap(int[] arr, int i, int length) {
        //i指向非叶子结点
        int temp = arr[i];
        //k指向非叶子结点的左孩子
        //因为length为要调整的元素个数,所以k<length
        //k = 2 * k + 1 过来要么是左要么是右下一趟要么调左要么调右,k如果为叶子结点(跳出循环)或者k没有破坏不用调整(进循环if全部跳过)
        //在调整三角(非叶子结点,非叶子结点左孩子,非叶子结点右孩子)后
        //假设max(非叶子结点,非叶子结点左孩子,非叶子结点右孩子) = maxValue不为非叶子结点
        //则下一趟还要调整三角(maxValue,maxValue左孩子,maxValue右孩子)
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            //如果k+1<length则表示左右结点都存在,否则,则表示右结点不存在
            if (k + 1< length && arr[k] < arr[k + 1]) {
                //k右移,指向右子结点
                k++;
            }
            //此时k指向的是max(左子结点,右子结点)
            //如果左(右)大于父结点
            if (arr[k] > temp) {
                //将max(左子结点,右子结点)赋值到父结点
                arr[i] = arr[k];
                //i指向与非叶子结点交换的左(右)孩子
                i = k;
            }else {
                break;
            }
        }
        //for循环结束,已经将以i为父结点的子树的最大值,放在了局部子树顶部
        //将非叶子结点赋值给它的左或右孩子结点
        arr[i] = temp;
    }
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值