堆排序

堆排序

  • 堆总是一颗完全二叉树

  • 堆中某个节点总是不大于(大顶堆)或不小于父节点(小顶堆)的值

    大顶堆 即 arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]。

    小顶堆 即 arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]。

上述,根节点从0开始,如果根节点从1开始,则左右节点分别为2i和2i+1
在这里插入图片描述
由上述性质可知:堆顶元素(或完全二叉树的根)必定是所有元素中最大值(大顶堆)或最小值(小顶堆)。

算法思想

大顶堆,将待排序的序列都成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点,将它移走(也就是将其与数组末尾元素交换,此时末尾元素就是最大值)然后将剩余的n-1个序列重新构成一个堆,这样就会得到n个元素中次小的值,如此反复。

算法过程

  1. 将无序列构建成一个堆,根据升序降序需求选择大顶堆或者小顶堆
  2. 将堆顶元素和末尾元素交换,将最大元素放到数组末尾
  3. 重新调整结构,使其满足堆定义然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序

大顶堆

  1. 将n个元素从0到n-1(或者从1到n)自顶向下、从左到右,转换成一个完全二叉树
  2. 从n/2的非叶子节点开始到根节点,逐个扫描,如果大于父节点就交换
  3. 直到根节点最大,如果树不满足最大堆的条件,继续调节,直到所有度节点都大于子节点为止

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H01G73QQ-1578996385491)(算法.assets/image-20200114163106883.png)]

构造大顶堆

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TESMJAcQ-1578996385495)(算法.assets/image-20200114163128144.png)]

  1. 从最后一个非叶子节点开始,第一个非叶子节点 arr.length/2-1=8/2-1=3,也就是97,在[97, 50]这个小堆里边,父节点97最大,所以不交换;
  2. 找到第2个非叶子节点,也就是位置为2的节点65,在[65, 13, 27]这个小堆里边,父节点65最大,所以不交换;
  3. 找到第3个非叶子节点,也就是位置为1的节点38,在[38, 97, 76]这个小堆里边,97最大,38和97交换;
  4. 找到第4个非叶子节点,也就是位置为0的节点49,在[49, 97, 65]这个小堆里边,97最大,49和97交换;
  5. 交换导致了子根[49, 38, 76]结构混乱,继续调整,[49, 38, 76]中76最大,49和76交换;
  6. 子根[38, 50]结构混乱,继续调整,[38, 50]中50最大,38和50交换;

堆排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jCgUnxiY-1578996385496)(算法.assets/image-20200114163434254.png)]

① 将堆顶元素97和末尾元素38进行交换,得到最大元素97;

② 重新调整结构,使其继续满足大顶堆的定义;

③ 再将堆顶元素76与末尾元素27进行交换,得到第二大元素76;

④ 重新调整结构,使其继续满足大顶堆的定义;

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

<?php

date_default_timezone_set('PRC');

function heapSort(&$arr){
    $len = count($arr);
    //先将数组一般小的构建大根堆
    for($i = floor($len /2) - 1 ; $i >=0;$i--){
        adjustHeap($arr,$i,$len);
    }
    // 调整堆结构+交换堆顶元素与末尾元素
    for ($j = $len - 1; $j > 0; $j--) {
        swap($arr, 0, $j);  // 将堆顶元素与末尾元素进行交换
        adjustHeap($arr, 0, $j); // 重新对堆进行调整
    } 
}

//调整堆
function adjustHeap(&$arr,$i,$length){
    $temp = $arr[$i];
    //97 65 38 49 $i ==》 3  2  1 0
//    print_r($tmp);
    //$k  ===> 7 5 3 7 1 3 7
    for($k = 2 * $i +1;$k < $length;$k = 2 * $k +1){
        if($k +1 < $length && $arr[$k] < $arr[$k+1]){
            $k++;
        }
        if($temp < $arr[$k]){
            $arr[$i] = $arr[$k]; // 将根节点设置为子节点的较大值
            $i = $k;             // 继续往下 
        }else{
            break;
        }
    }
    $arr[$i] = $temp;
}

function swap(&$arr,$a,$b){
    $temp = $arr[$a];
    $arr[$a] = $arr[$b];
    $arr[$b] = $temp; 
}

$arr = $arr = [49, 38, 65, 97, 76, 13, 27, 50];
heapSort($arr);
print_r($arr);

效率分析

1、时间复杂度:O(nlogn)

最坏,最好,平均时间复杂度均为O(nlogn)。

2、空间复杂度:堆排序仅需一个记录大小的供交换用的辅助存储空间,因此空间复杂度为O(1),是不稳定排序。

原文 大佬地址,本文翻译,如有不妥,请联系删除
https://www.cnblogs.com/sunshineliulu/p/8610645.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值