堆排序
-
堆总是一颗完全二叉树
-
堆中某个节点总是不大于(大顶堆)或不小于父节点(小顶堆)的值
大顶堆 即 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个元素中次小的值,如此反复。
算法过程
- 将无序列构建成一个堆,根据升序降序需求选择大顶堆或者小顶堆
- 将堆顶元素和末尾元素交换,将最大元素放到数组末尾
- 重新调整结构,使其满足堆定义然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
大顶堆
- 将n个元素从0到n-1(或者从1到n)自顶向下、从左到右,转换成一个完全二叉树
- 从n/2的非叶子节点开始到根节点,逐个扫描,如果大于父节点就交换
- 直到根节点最大,如果树不满足最大堆的条件,继续调节,直到所有度节点都大于子节点为止
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H01G73QQ-1578996385491)(算法.assets/image-20200114163106883.png)]
构造大顶堆
- 从最后一个非叶子节点开始,第一个非叶子节点 arr.length/2-1=8/2-1=3,也就是97,在[97, 50]这个小堆里边,父节点97最大,所以不交换;
- 找到第2个非叶子节点,也就是位置为2的节点65,在[65, 13, 27]这个小堆里边,父节点65最大,所以不交换;
- 找到第3个非叶子节点,也就是位置为1的节点38,在[38, 97, 76]这个小堆里边,97最大,38和97交换;
- 找到第4个非叶子节点,也就是位置为0的节点49,在[49, 97, 65]这个小堆里边,97最大,49和97交换;
- 交换导致了子根[49, 38, 76]结构混乱,继续调整,[49, 38, 76]中76最大,49和76交换;
- 子根[38, 50]结构混乱,继续调整,[38, 50]中50最大,38和50交换;
堆排序
① 将堆顶元素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