堆排序

基本介绍

1、堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏、最好、平均时间复杂度均为O(nlogn),是不稳定排序。

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

(1)大顶堆:每个结点的值都大于或等于其左右孩子结点的值,arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2](i 对应节点下标,i从0开始编号),升序使用大顶堆

(2)小顶堆:每个结点的值都小于或等于其左右孩子结点的值,arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2](i 对应节点下标,i从0开始编号),降序使用小顶堆

(3)注意:没有要求结点的左孩子的值和右孩子的值的大小关系

(4)堆是顺序储存的完全二叉树,即树是逻辑上的存储结构,数组是实际的存储结构

3、用下沉操作由 N 个元素构造堆只需少于 2N 次比较以及少于 N 次交换

4、将 N 个元素排序,堆排序只需少于 (2 * N * lgN + 2 * N) 次比较(以及一半次数的交换)

(1)2 * N 项来自于堆的构造

(2)2 * N * lgN 项来自于每次下沉操作最大可能需要 2 * lgN 次比较

5、堆排序在排序复杂性的研究中有着重要的地位

(1)因为它是我们所知的唯一能够同时最优地利用空间和时间的方法

(2)在最坏的情况下它也能保证使用 2 * N * lgN 次比较和恒定的额外空间

(3)当空间十分紧张的时候(例如在嵌入式系统或低成本的移动设备中)它很流行,因为它只用几行就能实现(甚至机器码也是)较好的性能

(4)但现代系统的许多应用很少使用它,因为它无法利用缓存

(5)数组元素很少和相邻的其他元素进行比较,因此缓存未命中的次数要远远高于大多数比较都在相邻元素间进行的算法,如快速排序、归并排序,甚至是希尔排序

 

实现思路(从小到大排序)

1.获取非叶子节点,每个非叶子节点的局部树(无序序列)构建成一个堆,根据升序、降序需求选择大顶堆或小顶堆

2.将堆顶元素与末尾元素交换,若为大顶堆,交换后的末尾元素为最大;若为小顶堆,交换后的末尾元素为最小

3.重新调整结构,使其满足堆定义,但交换后的堆底元素不参与调整

4.重复步骤2、步骤3,直到整个序列有序

 

获取最后一个非叶子节点下标

1.公式:arr.length/2 -1

2.推导

(1)arr.length为奇数,非叶子节点比叶子节点少一个,最后一个非叶子节点下标:(arr.length-1)/2-1

(2)arr.length为偶数,非叶子节点和叶子节点个数相等,最后一个非叶子节点下标:arr.length/2-1

(3)因为int取整型忽略小数,可以忽略奇偶,综上,最后一个非叶子节点下标:arr.length/2-1

 

代码实现

public class HeapSort {
    
    //堆排序(降序)
    public static void minheapSort(int arr[]) {
        int temp;
        //i代表非叶子节最大的下标,下标从大到小遍历
        //第一次交换前,调整为小顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            minHeap(arr, i, arr.length);
        }
        //将根节点与最后节点j交换
        for (int j = arr.length - 1; j > 0; j--) {
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            //j作为新树的剩余节点个数,即原树最后节点已经排序完成,原树倒数第二个节点作为新树的最后节点
            //从下标0的元素开始比从最后一个元素开始的效率高,因为只变动了头尾元素
            //而最后一个元素已经排序完毕,不进入新树,只需要变动根节点即可
            minHeap(arr, 0, j);
        }
    }

    /**
     * 功能:完成将以i对应的非叶子结点的树调整成小顶堆
     * arr:待调整的数组
     * i:表示非叶子结点在数组中索引
     * length:表示对多少个元素继续调整,length在逐渐的减少
     */
    public static void minHeap(int arr[], int i, int length) {
        int maxIndex = length - 1;//此时树中所有元素的最大下标
        int temp = arr[i];//先取出当前元素的值,保存在临时变量
        //k指向i的左子节点
        for (int k = i * 2 + 1; k <= maxIndex; k = k * 2 + 1) {
            //k+1(右子节点下标)<=maxIndex(树的最大下标),说明存在右子节点
            //arr[k]>arr[k+1]说明右子结点的值小于左子结点的值
            if (k + 1 <= maxIndex && arr[k] > arr[k + 1]) {
                k++; //k指向i的右子结点
            }
            if (arr[k] < temp) {//如果子结点小于父结点
                arr[i] = arr[k];//把较大的值赋给父节点
                i = k; //i指向k,继续循环比较,向下调整结构
            } else {
                break;
            }
        }
        //当循环结束后,已经将以i为父结点的树的最小值,放在了最顶(局部)
        arr[i] = temp;// 将temp值放到调整后的位置
    }

    //堆排序(升序)
    public static void maxheapSort(int arr[]) {
        int temp;
        //i代表非叶子节最大的下标,下标从大到小遍历
        //第一次交换前,调整为大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            maxHeap(arr, i, arr.length);
        }
        //将根节点与最后节点j交换
        for (int j = arr.length - 1; j > 0; j--) {
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            //j作为新树的剩余节点个数,即原树最后节点已经排序完成,原树倒数第二个节点作为新树的最后节点
            //从下标0的元素开始比从最后一个元素开始的效率高,因为只变动了头尾元素
            //而最后一个元素已经排序完毕,不进入新树,只需要变动根节点即可
            maxHeap(arr, 0, j);
        }
    }

    /**
     * 功能:完成将以i对应的非叶子结点的树调整成大顶堆
     * arr:待调整的数组
     * i:表示非叶子结点在数组中索引
     * length:表示对多少个元素继续调整,length在逐渐的减少
     */
    public static void maxHeap(int arr[], int i, int length) {
        int maxIndex = length - 1;//此时树中所有元素的最大下标
        int temp = arr[i];//先取出当前元素的值,保存在临时变量
        //k指向i的左子节点
        for (int k = i * 2 + 1; k <= maxIndex; k = k * 2 + 1) {
            //k+1(右子节点下标)<=maxIndex(树的最大下标),说明存在右子节点
            //arr[k]<arr[k+1]说明右子结点的值大于左子结点的值
            if (k + 1 <= maxIndex && arr[k] < arr[k + 1]) {
                k++; //k指向i的右子结点
            }
            if (arr[k] > temp) {//如果子结点大于父结点
                arr[i] = arr[k];//把较大的值赋给父节点
                i = k; //i指向k,继续循环比较,向下调整结构
            } else {
                break;
            }
        }
        //当循环结束后,已经将以i为父结点的树的最大值,放在了最顶(局部)
        arr[i] = temp;// 将temp值放到调整后的位置
    }
}

 

posted @   半条咸鱼  阅读(68)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示