今天比較有感悟的是吳軍老師《矽谷來信》的一句話:走完最後的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
複製程式碼
-------------------------華麗的分割線--------------------
看完的朋友可以點個喜歡/關注,您的支援是對我最大的鼓勵。
想了解更多,歡迎關注我的微信公眾號:番茄技術小棧