排序--堆排序

版权声明:本文源自简书tianma,转载请务必注明出处:http://www.jianshu.com/p/ad3082312012

概念

: 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子(如果存在的话)的值,称为最大堆;或者每个节点的值都小于或等于其左右孩子(如果存在的话)的值,称为最小堆。

完全二叉树:

complete binary tree

最大堆:


max heap

最小堆:

min heap

图片来源: 常见排序算法 - 堆排序 (Heap Sort)

堆排序:利用堆(这里使用最大堆)进行排序的方法。其基本思想是:将待排序的序列构造成一个最大堆,此时待排序序列的最大值就是堆顶的根节点,将其移走(其实就是将其与待排序序列的最后一个元素进行交换,此时待排序序列最后一个元素就是最大值),然后将剩余的序列重新构造成一个堆,如此反复,直到待排序序列只有一个元素为止,则排序完成。

性质

已知arr[0...n-1]是长度为n的最大堆数组,下标从0开始,那么对于下标为i的节点I,有:
(1). 如果I的左孩子存在的话,那么I的左孩子节点的下标为 left(i) = 2*i+1;
(2). 如果I的右孩子存在的话,那么I的右孩子节点的下标为 right(i) = 2*i+2;
(3). 如果I双亲节点存在的话,那么I的双亲节点的下标为 parent(i) = (i-1)/2; (向下取整)

基本操作
  1. 构建最大堆 buildMaxHeap(int[] arr):将待排序序列arr构建成最大堆;
  2. 调整最大堆 adjustHeap(int arr[], int begin, int end): 已知arr[begin]的左子树和右子树都满足最大堆,那么调节节点arr[begin],将以arr[begin]为根节点的二叉树调整为最大堆。

对于堆排序,最重要的就是构建最大堆和调整最大堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。

Java实现
// 定义接口
interface Sorter {
    /**
     * 将数组按升序排序
     */
    int[] sort(int[] arr);

    /**
     * 交换数组arr中的第i个位置和第j个位置的关键字
     */
    default void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}
// 堆排序
class HeapSorter implements Sorter {

    @Override
    public int[] sort(int[] arr) {
        heapSort(arr);
        return arr;
    }

    private void heapSort(int[] arr) {

        buildMaxHeap(arr); // 构建最大堆

        // 将最大堆堆顶元素与数组末尾元素交换,并将前n-1序列重新构造成最大堆,重复n-1次
        for (int i = arr.length - 1; i >= 1; i--) {
            swap(arr, 0, i); // 将堆顶元素和当前未经排序的子序列的最后一个元素进行交换
            adjustHeap(arr, 0, i - 1); // 将arr[0...i-1](前i个元素)重新调整为最大堆
        }
    }

    /**
     * 将指定数组arr构建成最大堆
     */
    private void buildMaxHeap(int[] arr) {
        int len = arr.length;
        // 从最后一个非叶子节点往前遍历,将当前序列构成最大堆
        for (int i = len / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, len - 1);
        }
    }

    /**
     * 假定arr[begin]的左子树和右子树均满足最大堆,那么调节节点arr[begin],将以arr[begin]为根节点的二叉树调整为最大堆。
     */
    private void adjustHeap(int[] arr, int begin, int end) {
        int tmp = arr[begin];
        int j;
        for (j = 2 * begin + 1; j <= end; j = 2 * j + 1) {
            // j=2*begin+1表示j对应二叉树节点的左孩子
            if (j + 1 <= end && arr[j] < arr[j + 1]) {
                // 如果当前节点的右孩子存在且左孩子的值小于右孩子
                j++; // j为左右孩子较大记录的下标
            }
            if (tmp >= arr[j]) // tmp的值已经大于arr[j],则调整完毕,跳出循环
                break;
            arr[begin] = arr[j]; // 当前根节点并未均大于左右节点(如果有的话),重新给当前根节点赋值
            begin = j; // begin指向新的可能需要进行最大堆调整的子树的根节点
        }
        arr[begin] = tmp;
    }
}

堆排序其实也是一种选择排序,是一种树形选择排序。在简单选择排序中,从arr[0...n-1]中选择最小(或最大)记录,需要比较n-1次,然后再从剩下arr[1...n-1]的n-1个元素中选择最小(或最大)记录,需要比较n-2次。然而事实上这n-2次比较中,有许多已经在前一趟n-1次的比较中做过了;而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数,提高算法效率。

复杂度

时间复杂度:
对于n个关键字序列,每个节点需比较log2(n)次,因此其时间复杂度为O(nlogn)。
由于堆排序对原始记录的排序状态并不敏感,所以它的最好、最坏、平均时间复杂度均为O(nlogn)
由于初始构建堆所需要的比较次数较多,所以堆排序不适合待排序序列个数少的情况。

空间复杂度:
最好情况=平均情况=最坏情况=O(1)

参考

推排序
常见排序算法 - 堆排序 (Heap Sort)
堆排序(Heap Sort)算法学习

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容

  • 总结一下常见的排序算法。 排序分内排序和外排序。内排序:指在排序期间数据对象全部存放在内存的排序。外排序:指在排序...
    jiangliang阅读 1,337评论 0 1
  • -希尔排序 克服插入排序每次只能交换一对元素的缺点5-间隔的排序,3-间隔的排序,1-间隔排序(最后必须是1-间隔...
    Spicy_Crayfish阅读 644评论 0 0
  • 一、什么是堆? 首先是一棵完全二叉树; 大顶堆:每个结点的值都大于或等于其左右孩子结点的值; 小顶堆:每个结点的值...
    liangxifeng833阅读 469评论 0 2
  • proud2008阅读 412评论 1 0
  • 心中的花 满眼的蓝天,深邃的让人感动;绿绿的草地,上面是密密匝匝的格桑花疯了似的开放,生长,万...
    海深深阅读 214评论 1 0