堆排序(php實現)

吳軍旗發表於2018-01-17

主要藉助上一篇博文實現的堆, 進行堆排序,然後考慮堆排序可以優化的點。

原文請訪問我的部落格番茄技術小棧

簡單的堆排序

定義(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
複製程式碼

-------------------------華麗的分割線--------------------

看完的朋友可以點個喜歡/關注,您的支援是對我最大的鼓勵。

個人部落格番茄技術小棧掘金主頁

想了解更多,歡迎關注我的微信公眾號:番茄技術小棧

番茄技術小棧

相關文章