企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### 基本原理 利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。堆排序算法就是抓住了堆的这一特点,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最大堆,依次类推,最终得到排序的序列。 ![](https://box.kancloud.cn/acf7bbd2a6bc65325f6af1d7a19a3d02_1792x776.png) * * * * * ### 动画演示 ![](https://box.kancloud.cn/658d0f58eed41a5c11cd1d1c039269ba_547x364.gif) * * * * * ### 算法步骤 1. 创建一个堆 H[0...n-1]; 2. 把堆首(最大值)和堆尾互换; 3. 把堆的尺寸缩小1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置; 4. 重复步骤 2,直到堆的尺寸为 1; ![](https://box.kancloud.cn/dfe2297f81cab0a4120ab385613cebee_660x323.jpg =600x) * * * * * ### 代码实现 ``` function heapAdjust(&$arr, $s, $m) { $tmp = $arr[$s]; // 在调整为大根堆的过程中可能会影响左子堆或右子堆 // 循环的作用是要保证子堆也是大根堆 for($j = 2*$s + 1; $j <= $m; $j = 2*$j + 1) { // 找到根节点的左右孩子中的最大者,然后用这个最大者与根节点比较, // 若大则进行调整,否则符合大根堆的 特点跳出循环 if($j < $m && $arr[$j] < $arr[$j+1]) { $j++; } if($tmp >= $arr[$j] ) { break; } $arr[$s] = $arr[$j]; $s = $j; } $arr[$s] = $tmp; } // 堆排序 function heapSort($arr) { $len = count($arr); // 依次从子堆开始调整堆为大根堆 for($i = floor($len/2-1); $i >= 0; $i--) { heapAdjust($arr, $i, $len-1); } // 依次把根节点调换至最后一个位置,再次调整堆为大根堆,找到次最大值, // 依次类推得到一个有序数组 for($n = $len-1; $n > 0; $n--) { $tmp = $arr[$n]; $arr[$n] = $arr[0]; $arr[0] = $tmp; heapAdjust($arr, 0, $n-1); } return $arr; } // 调用测试 $arr = [3,1,13,5,7,11,2,4,14,9,150,6,12,10,8]; print_r(heapSort($arr)); ``` * * * * * ### 平均时间复杂度:O(n2) 堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。 平均性能 :O(n * logn) 其他性能 :由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。 * * * * * ### 小结 我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n /2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。