原地堆排序
Posted 番茄技术小栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原地堆排序相关的知识,希望对你有一定的参考价值。
做了太久的小程序,6月份之后开始去年的算法的学习,今天比较有感悟的是吴军老师《硅谷来信》的一句话:走完最后的1%,这很重要。主要介绍一下原地堆排序算法以及实现(php)
解释
上一次的博客 算法与数据结构堆和堆排序之堆排序 两种堆排序都需要开辟O(n)的辅助空间(构造函数中使用new分配的辅助空间),程序在开辟辅助空间和释放空间的时候也会消耗一定的时间,若能多数组进行原地堆排序,则省去了开辟和释放空间的时间,时间性能会好一些。
思路
给定一个大小为n的数组,将这个数组heapify,变为最大堆,此时数组的第一个元素就是最大值,将该值与数组最后一个元素交换位置后,对前n-1个元素进行heapify,变成最大堆,将第一个元素与数组倒数第二个元素交换位置...以此类推,最后得到的数组就是从小到大排序的。
注意*
上篇博客中堆排序堆的实现是从1开始,所以当前元素(下标 i)的双亲下标为 i/2,左孩子下标为 2i,右孩子下标为 2i+1;而原地堆排序中直接对数组进行操作,数组的下标是从0开始,所以当前元素(下标i)的双亲为 (i-1)/2,左孩子下标为 2i+1,右孩子下标为 2i+2。
实现
<?php
require('../SortingAdvance/QuickSort.php');
/**
* 原地堆排序
*/
function shiftDown(&$arr, $n, $i){
//heapify最后一个非叶子节点, 最后一个节点为:$n-1
while( 2*$i + 1 < $n ){
//左节点
$j = 2*$i + 1;
//判断右节点是否存在,并且右节点大于左节点
if( $j+1 < $n && $arr[$j+1] > $arr[$j] ) $j ++;
if( $arr[$i] >= $arr[$j] ) break;
// swap( $arr[$i] , $arr[$j] );
swap( $arr, $i , $j );
$i = $j;
// print_r($arr);
}
}
function selfHeadSort(&$arr, $n){
//叶子节点已经heapify了,所以从(n-1)/2的非叶子节点开始
for ($i=(int)(($n-1)/2); $i >=0 ; $i--) {
shiftDown($arr, $n, $i);
}
//$arr已经是一个堆了,下面进行原地堆排序
//从最后一个元素开始,先和第一个元素交换,然后再对第一个元素heapify
for ($i=$n-1; $i > 0 ; $i--) {
swap( $arr, 0 , $i);
shiftDown($arr, $i, 0);
}
}
$n = 10000;
$arr = generateRandomArray($n, 0, $n);
$copy_arr1 = $arr;
$copy_arr2 = $arr;
$copy_arr3 = $arr;
$copy_arr4 = $arr;
testSort("selfHeadSort", "selfHeadSort", $arr, $n);
testSort("mergeSort", "mergeSort", $copy_arr1, $n);
testSort("quickSort", "quickSort", $copy_arr2, $n);
testSort("quickSort2", "quickSort2", $copy_arr3, $n);
testSort("quickSort3", "quickSort3", $copy_arr4, $n);
?>
时间损耗
selfHeadSort运行的时间为:0.062602043151855s
mergeSort运行的时间为:0.670814037323s
quickSort运行的时间为:0.033109188079834s
quickSort2运行的时间为:0.021806955337524s
quickSort3运行的时间为:0.054163932800293s
以上是关于原地堆排序的主要内容,如果未能解决你的问题,请参考以下文章
挖掘算法中的数据结构:堆排序之 二叉堆(Heapify原地堆排序优化)