主要藉助上一篇博文實現的堆, 進行堆排序,然後考慮堆排序可以優化的點。
原文請訪問我的部落格番茄技術小棧
簡單的堆排序
定義(wiki)
堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
簡單思路
有了前一篇部落格實現的堆,自然而然的想到,將給出的陣列先形成一個大根堆,然後逐一的取出,後逆序的返回即可。
程式碼實現
<?php
// require('../Library/SortTestHelper.php');
require('../SortingAdvance/QuickSort.php');
/**
* 最大堆
*/
class MaxHeap{
private $data;
private $count;
public function __construct(){
$this->data = array();
$this->count = 0;
}
// public function __construct($arr){
// }
public function insert($item){
//從1開始
$this->data[$this->count + 1] = $item;
$this->_shiftUp($this->count+1);
$this->count++;
}
public function extractMax(){
$ret = $this->data[1];
swap( $this->data, 1 , $this->count);
$this->count--;
$this->_shiftDown(1);
return $ret;
}
public function getMax(){
return $this->data[1];
}
public function isEmpty(){
return $this->count == 0;
}
public function getData(){
return $this->data;
}
/**
* [_shiftUp 新加入到堆中的元素直接放在陣列後面,再與父元素比較後交換位置,直到根節點]
* @param [type] $k [description]
* @return [type] [description]
*/
private function _shiftUp($k){
//如果葉子節點的值比父元素大交換位置,並更新k的值
while( $k > 1 && $this->data[(int)($k/2)] < $this->data[$k] ){
// swap( $this->data[(int)($k/2)], $this->data[$k] );
swap( $this->data, (int)($k/2) , $k);
$k = (int)($k/2);
}
}
/**
* [_shiftDown 元素出堆的時候,需要維護此時的堆依然是一個大根堆, 此時將陣列元素的最後一個值與第一個值交換,後從上往下維護堆的性質]
* @param [type] $k [description]
* @return [type] [description]
*/
private function _shiftDown($k){
//2k代表該節點的左子節點
while( 2*$k <= $this->count ){
$j = 2*$k;
//判斷右節點是否存在,並且右節點大於左節點
if( $j+1 <= $this->count && $this->data[$j+1] > $this->data[$j] ) $j ++;
if( $this->data[$k] >= $this->data[$j] ) break;
// swap( $this->data[$k] , $this->data[$j] );
swap( $this->data, $k , $j );
$k = $j;
}
}
}
//簡單的堆排序
function headSort1(&$arr, $n){
$head_obj = new MaxHeap();
for ($i=0; $i < $n; $i++) {
$head_obj->insert($arr[$i]);
}
//逆序輸出
for ($i=$n-1; $i >= 0; $i--) {
$arr[$i] = $head_obj -> extractMax();
}
}
$n = 10000;
$arr = generateRandomArray($n, 0, $n);
$copy_arr1 = $arr;
$copy_arr2 = $arr;
$copy_arr3 = $arr;
$copy_arr4 = $arr;
// $arr = generateNearlyOrderedArray($n, 100);
testSort("headSort1", "headSort1", $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);
?>
複製程式碼
結果
headSort1執行的時間為:0.70801091194153s
mergeSort執行的時間為:0.94017505645752s
quickSort執行的時間為:0.30204510688782s
quickSort2執行的時間為:0.19032001495361s
quickSort3執行的時間為:0.36022400856018s
複製程式碼
結果分析
for ($i=0; $i < $n; $i++) {
$head_obj->insert($arr[$i]);
}
//逆序輸出
for ($i=$n-1; $i >= 0; $i--) {
$arr[$i] = $head_obj -> extractMax();
}
複製程式碼
這段程式碼進行了兩次O(NlogN)時間複雜度的運算,並且是將已有的陣列去生成一個堆,然後藉助這個堆重新賦值給陣列,其實可以直接用陣列去形成一個堆,然後我們就可以進行一次O(NlogN)複雜度的運算。我們只需要重寫一個建構函式,通過一個陣列去構造它!
堆排序的優化
程式碼實現
<?php
require('../SortingAdvance/QuickSort.php');
/**
* 根據已知陣列最大堆
*/
class HeapSort{
private $data;
private $count;
public function __construct($arr, $n){
$this->data = array();
$this->count = $n;
for ($i=0; $i < $n; $i++) {
//從1開始
$this->data[$i+1] = $arr[$i];
}
//葉子節點已經是一顆大根堆了,從最後一個非葉子節點進行_shiftDown,知道根節點
for ($i= (int)($n/2); $i >= 1 ; $i--) {
$this->_shiftDown($i);
}
}
// public function __construct($arr){
// }
public function insert($item){
//從1開始
$this->data[$this->count + 1] = $item;
$this->_shiftUp($this->count+1);
$this->count++;
}
public function extractMax(){
$ret = $this->data[1];
swap( $this->data, 1 , $this->count);
$this->count--;
$this->_shiftDown(1);
return $ret;
}
public function getMax(){
return $this->data[1];
}
public function isEmpty(){
return $this->count == 0;
}
public function getData(){
return $this->data;
}
/**
* [_shiftUp 新加入到堆中的元素直接放在陣列後面,再與父元素比較後交換位置,直到根節點]
* @param [type] $k [description]
* @return [type] [description]
*/
private function _shiftUp($k){
//如果葉子節點的值比父元素大交換位置,並更新k的值
while( $k > 1 && $this->data[(int)($k/2)] < $this->data[$k] ){
// swap( $this->data[(int)($k/2)], $this->data[$k] );
swap( $this->data, (int)($k/2) , $k);
$k = (int)($k/2);
}
}
/**
* [_shiftDown 元素出堆的時候,需要維護此時的堆依然是一個大根堆, 此時將陣列元素的最後一個值與第一個值交換,後從上往下維護堆的性質]
* @param [type] $k [description]
* @return [type] [description]
*/
private function _shiftDown($k){
//2k代表該節點的左子節點
while( 2*$k <= $this->count ){
$j = 2*$k;
//判斷右節點是否存在,並且右節點大於左節點
if( $j+1 <= $this->count && $this->data[$j+1] > $this->data[$j] ) $j ++;
if( $this->data[$k] >= $this->data[$j] ) break;
// swap( $this->data[$k] , $this->data[$j] );
swap( $this->data, $k , $j );
$k = $j;
}
}
}
function headSort2(&$arr, $n){
$head_obj = new HeapSort($arr, $n);
for ($i=$n-1; $i >= 0; $i--) {
$arr[$i] = $head_obj -> extractMax();
}
}
$n = 10000;
$arr = generateRandomArray($n, 0, $n);
$copy_arr1 = $arr;
$copy_arr2 = $arr;
$copy_arr3 = $arr;
$copy_arr4 = $arr;
testSort("headSort2", "headSort2", $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);
?>
複製程式碼
結果
quickSort2執行的時間為:0.12423086166382s
quickSort3執行的時間為:0.051468849182129s
headSort2執行的時間為:0.033907890319824s
mergeSort執行的時間為:0.020761013031006s
quickSort執行的時間為:0.016165018081665s
quickSort2執行的時間為:0.01316499710083s
quickSort3執行的時間為:0.026669025421143s
複製程式碼
-------------------------華麗的分割線--------------------
看完的朋友可以點個喜歡/關注,您的支援是對我最大的鼓勵。
想了解更多,歡迎關注我的微信公眾號:番茄技術小棧