堆排序算法與PHP實現


堆排序有點小復雜,分成三塊:

第一塊,什么是堆,什么是最大堆

第二塊,怎么將堆調整為最大堆,這部分是重點

第三塊,堆排序介紹

 

第一塊,什么是堆,什么是最大堆

什么是堆

這里的堆(二叉堆),指得不是堆棧的那個堆,而是一種數據結構。

堆可以視為一棵完全的二叉樹,完全二叉樹的一個“優秀”的性質是,除了最底層之外,每一層都是滿的,這使得堆可以利用數組來表示,每一個結點對應數組中的一個元素.

數組與堆之間的關系

二叉堆一般分為兩種:最大堆和最小堆。

什么是最大堆

堆中每個父節點的元素值都大於等於其孩子結點(如果存在),這樣的堆就是一個最大堆

因此,最大堆中的最大元素值出現在根結點(堆頂)

節點與數組索引關系

對於給定的某個結點的下標i,可以很容易的計算出這個結點的父結點、孩子結點的下標,而且計算公式很漂亮很簡約

第二塊,怎么將堆調整為最大堆,這部分是重點

整個過程如下圖所示

在4,14,7這個小堆里邊,父節點4小於左孩子14,所以兩者交換

在4,2,8這個小堆里邊,父節點4小於右孩子8,所以兩者交換

上圖展示了一趟調整的過程,這個過程遞歸實現,直到調整為最大堆為止

第三塊,堆排序介紹

堆排序就是把堆頂的最大數取出,

將剩余的堆繼續調整為最大堆,具體過程在第二塊有介紹,以遞歸實現

剩余部分調整為最大堆后,再次將堆頂的最大數取出,再將剩余部分調整為最大堆,這個過程持續到剩余數只有一個時結束

下邊三張圖詳細描述了整個過程

 

具體PHP實現:

/**
 * 使用異或交換2個值,原理:一個值經過同一個值的2次異或后,原值不變
 * @param int $a
 * @param int $b
 */
function swap(&$a,&$b){
	$a = $a^$b;
	$b = $a^$b;
	$a = $a^$b;
}

/**
 * 整理當前樹節點($n),臨界點$last之后為已排序好的元素
 * @param int $n
 * @param int $last
 * @param array $arr
 * 
 */
function adjustNode($n,$last,&$arr){
	$l = $n<<1;	// 左孩子
	if( !isset($arr[$l])||$l>$last ){
		return ;
	}
	$r = $l+1;	// 右孩子
	// 如果右孩子比左孩子大,則讓父節點與右孩子比
	if( $r<=$last&&$arr[$r]>$arr[$l] ){
		$l = $r;
	}
	// 如果其中子節點$l比父節點$n大,則與父節點$n交換
	if( $arr[$l]>$arr[$n] ){
		swap($arr[$l],$arr[$n]);
		// 交換之后,父節點($n)的值可能還小於原子節點($l)的子節點的值,所以還需對原子節點($l)的子節點進行調整,用遞歸實現
		adjustNode($l, $last, $arr);
	}
}

/**
 * 堆排序(最大堆)
 * @param array $arr
 */
function heapSort(&$arr){
	// 最后一個蒜素位
	$last = count($arr);
	// 堆排序中常忽略$arr[0]
	array_unshift($arr, 0);
	// 最后一個非葉子節點
	$i = $last>>1;
	// 整理成最大堆,最大的數放到最頂,並將最大數和堆尾交換,並在之后的計算中,忽略數組最后端的最大數(last),直到堆頂(last=堆頂)
	while(true){
		adjustNode($i, $last, $arr);
		if( $i>1 ){
			// 移動節點指針,遍歷所有節點
			$i--;
		}
		else{
			// 臨界點$last=1,即所有排序完成
			if( $last==1 ){
				break;
			}
			swap($arr[$last],$arr[1]);
			$last--;
		}
	}
	// 彈出第一個元素
	array_shift($arr);
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM