排序算法之交換排序類


冒泡排序

一、冒泡排序的原理

 

 

注意:

1、其實原理就是相鄰的兩兩比較,我們一從小到大為列,誰小誰就在前面;

2、比較完了之后互相要交換值,這個時候通過第三個變量進行交換;

3、比如我們現在數組的大小為5,那么我們就要進行4輪的比較,上面粘貼的實例就是大小為4,經過3輪比較。

二、源代碼

<?php
	function bubbleSort($arr)
	{
		$length = count($arr);
		$count1 = 0;
		$count2 = 0;
		//$flag = 1;
		for($i = 0; $i < $length - 1; $i++){
			for($j = $length - 1; $j > $i; $j--){
				$count1++;	
				$flag = 0;
				if($arr[$j-1] > $arr[$j]){
					$count2++;	
					$temp = $arr[$j-1];
					$arr[$j-1] = $arr[$j];
					$arr[$j] = $temp;	
					//$flag = 1;
				}
			}
		}
		echo "一共進行了".$count1.'比較和'.$count2.'次移動';
		var_dump($arr);
	}
	$arr = [5,2,6,0,3,9,1,7,4,8];
	bubbleSort($arr);

 

三、算法優化

  1、設置標志變量,目的在於判斷當前有沒有值交換,沒有就不交換了,就直接下一輪。上面列子中的第二輪完全沒有在交換值,這樣會影響效率。

<?php
	function bubbleSort($arr)
	{
		$length = count($arr);
		$count1 = 0;
		$count2 = 0;
		$flag = 1;
		for($i = 0; $i < $length - 1 && $flag; $i++){
			for($j = $length - 1; $j > $i; $j--){
				$count1++;	
				$flag = 0;
				if($arr[$j-1] > $arr[$j]){
					$count2++;	
					$temp = $arr[$j-1];
					$arr[$j-1] = $arr[$j];
					$arr[$j] = $temp;	
					$flag = 1;
				}
			}
		}
		echo "一共進行了".$count1.'比較和'.$count2.'次移動';
		var_dump($arr);
	}
	$arr = [5,2,6,0,3,9,1,7,4,8];
	bubbleSort($arr);

這樣的話多余的比較就不在比較了,這樣就減少了比較的次數;

四、性能分析

排序類別 排序方法 時間復雜度 空間復雜度 穩定性
平均情況 最壞情況 最好情況
交換排序 冒泡排序 O(n^2) O(n^2) O(n) O(1) 穩定

 

快速排序

一、快速排序的實現流程

二、快速排序的工作原理

原理:指針之間的移動,我們選擇一個基准值,當前的值比基准值大我們通過指針的移動將它放在最右邊否則放在最左邊,圖解

 例如我們的目標數組是

$arr = [5,2,6,0,3,9,1,7,4,8]

選取的基准值是

$low = 0;
$high = 10;
$point = 5;

第一步:我們選出比基准值小的值,所以我們開始尋找比5小的值,從$high開始,從右向左找,不斷的遞減$high的值,我們找到了下標8比基准值小的值,這個時候我們將數據4移動到下標為0位置,將數據5移動到下標為8的位置,完成了第一次的比較

 

$low = 0;
$high = 8;
$point = 5;

 

第二步:我們選出比基准值大的值,所以我們尋找比5大的值,從$low開始,從左向右尋找,不斷遞增$low的值,我們找到了下標2比基准值大的值,,這個時候我們將數據為6的下標和數據為5的下標進行互換,也就是下標2和8進行互換;

$low = 3;
$high = 8;
$point = 5;

后面的操作將執行第一步和第二步,直到不能在分組整個程序終止。

圖解

 

注意:

1、我們不是一步就得到結果,第一輪結束,我們要對第二輪在進行上述過程,最終得到答案。

2、我們在進行比基准值小的時候,是從右向左開始的(數組下標最右邊是數組最大的下標),反之從左向右;理解比基准值大的放在最右邊,比基准值小的放在最左邊;

3、他的掃描過程是最兩端向中間進行掃描。

三、源代碼

<?php
	function swap(array &$arr, $a, $b)
	{
		$temp = $arr[$a];
		$arr[$a] = $arr[$b];
		$arr[$b] = $temp;
	}

	function Partition(array &$arr, $low, $high)
	{
		$point = $arr[$low];
		while($low < $high){//從數組的兩端交替向中間掃描
			while($low < $high && $arr[$high] >= $point){
				$high--;//過濾掉比基准值大的(不需要移動)
			}
			swap($arr, $low, $high);//遇到比基准值小的放在數組最左邊(這個就是需要交換位置的)
			while($low < $high && $arr[$low] <= $point){
				$low++;//過濾掉比基准值小的(不需要移動)
			}
			swap($arr, $low, $high);//遇到比基准值大的放在數組的最右邊
		}
		return $low;
	}
	//快速排序的實現方法
	function Qsort(array &$arr, $low, $high)
	{
		if($low < $high){
			$point = Partition($arr, $low, $high);//計算基准值
			Qsort($arr, $low, $point - 1);//對比基准值較小的一部分進行快速排序,也就是移動到數組的左面
			Qsort($arr, $point + 1, $high);//對比基准值較大的一部分進行排序,也就是移動到數組的右面
		}
	}
	//快速排序的主函數
	function QuickSort(array &$arr){
		$low = 0;
		$high = count($arr) - 1;
		Qsort($arr, $low, $high);
	}
	$arr = array(9,1,5,8,3,7,4,6,2);
	QuickSort($arr);
	var_dump($arr);

 四、快速排序的優化

 1、優化選取基准點

三數取中法,我們首先選取目標數組的最大下標、最小下標、中間下標的值,然后對他們進行排序找到中間的那個值作為基准值。

 優化的代碼:

function Partition(array &$arr, $low, $high)
	{
		$mind = $low + ($high - $low)/2;
		if($arr[$low] > $arr[$high]){
			swap($arr, $low, $high);
		}
		if($arr[$mind] > $arr[$high]){
			swap($arr, $mind, $high);
		}
		if($arr[$mind] > $arr[$low]){
			swap($arr, $mind, $low); 
		}
		$point = $arr[$low];
		while($low < $high){//從數組的兩端交替向中間掃描
			while($low < $high && $arr[$high] >= $point){
				$high--;//過濾掉比基准值大的(不需要移動)
			}
			swap($arr, $low, $high);//遇到比基准值小的放在數組最左邊(這個就是需要交換位置的)
			while($low < $high && $arr[$low] <= $point){
				$low++;//過濾掉比基准值小的(不需要移動)
			}
			swap($arr, $low, $high);//遇到比基准值大的放在數組的最右邊
		}
		return $low;
	}

2、優化不必要的交換

因為指針來回在移動,這樣會帶來性能的開銷

function Partition(array &$arr, $low, $high)
	{
		$point = $arr[$low];
		while($low < $high){//從數組的兩端交替向中間掃描
			while($low < $high && $arr[$high] >= $point){
				$high--;//過濾掉比基准值大的(不需要移動)
			}
			$arr[$low] = $arr[$high];
			while($low < $high && $arr[$low] <= $point){
				$low++;//過濾掉比基准值小的(不需要移動)
			}
			$arr[$high] = $arr[$low];
		}
		$arr[$low] = $point;
		return $low;
	}

3、優化小數組的排序方案 

 當我們排序的數據比較小的時候我們直接用直接插入排序,當數據量大的時候用快速排序是很有效的;這個時候我們要定義數組的大小,數組在多少范圍內就用直接插入排序,否則用快速排序。

4、優化遞歸操作

我們利用偽遞歸來解決遞歸的性能,就是減小遞歸的次數,將遞歸棧的空間充分利用起來。

//快速排序的實現方法
	function Qsort(array &$arr, $low, $high)
	{
		if($low < $high){
			while ($low < $high) {
				$point = Partition($arr, $low, $high);//計算基准值
				if($point - $low < $high - $point){
					Qsort($arr, $low, $point - 1);//對比基准值較小的一部分進行快速排序,也就是移動到數組的左面
					$low = $point + 1;
					//Qsort($arr, $point + 1, $high);
				}else{
					Qsort($arr, $point+1, $high);//對比基准值較小的一部分進行快速排序,也就是移動到數組的左面
					$high = $point - 1;
					//Qsort($arr, $low, $point - 1)
				}
				
			}
		}
	}

五、性能分析

 

排序類別 排序方法 時間復雜度 空間復雜度 穩定性
平均情況 最壞情況 最好情況
交換排序 快速排序 O(n*log2n) O(n^2)   O(log2n)-O( n ) 不穩定


總結

1、快速排序是冒泡排序的升級;

2、快速排序和冒泡排序都稱之為交換類排序,他們都需要數據之間的交換;快速排序是指針之間的交換而冒泡排序是數值之間的交換。

  


免責聲明!

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



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