当前位置: 首页 > 后端技术 > PHP

【PHP实现算法】快速排序的几种实现

时间:2023-03-30 00:00:39 PHP

什么是快速排序?快速排序采用分而治之的方法,通过一次遍历将待排序的数据分成两个独立的部分,并且一部分中的所有数据都比另一部分中的所有数据都小,然后使用同样的方法对两部分数据进行快速排序。它的过程是这样选择一个比较基值,然后把数组中小于这个值的元素放在左边,大于这个值的元素放在右边。对左右数组重复此步骤,直到不能再分割为止,然后对数组进行排序。常用的实现方式先来看第一种实现方式,也是比较常用的一种方式。一个临时数组用于分别存储左元数和左元素。我们选择第一个元数作为基值。函数quickSortByTempArr(&$arr){$length=count($arr);如果($length<=1)返回;$左=$右=[];对于($i=1;$i<$length;$i++){如果($arr[$i]<$arr[0]){$left[]=$arr[$i];}else{$right[]=$arr[$i];}}quickSortByTempArr($left);quickSortByTempArr($right);$arr=array_merge($left,[$arr[0]],$right);}这个方法符合分而治之的思想。缺点是应用临时数组分别存储左右元素。空间复杂度为O(n)再来看第二种方法,采用元素交换法,选择中间的元素(或选择第一个元素)作为参考值,每次比较交换左边较小的元素,和较大的元素向右交换。functionquickSort(&$arr,$start,$end){如果($start>=$end)返回;$smaller=$start;$更大=$结束;$middle=$start+($end-$start)/2;while($smaller<=$bigger){//如果较小,则留在左边if($arr[$smaller]<$arr[$middle]){if($smaller>$middle){list($arr[$smaller],$arr[$middle])=[$arr[$middle],$arr[$smaller]];$middle=$smaller;}$更小++;继续;}//重复元素if($arr[$smaller]==$arr[$middle]){$middle=$smaller;$更小++;继续;}//如果更大,就和最右边的未分类数据交换//右边都是排列好的Sequence,直接和中间交换if($bigger<=$middle){list($arr[$smaller],$arr[$middle])=[$arr[$middle],$arr[$smaller]];$middle=$smaller;}else{list($arr[$smaller],$arr[$bigger])=[$arr[$bigger],$arr[$smaller]];}$更大--;}quickSort($arr,$start,$middle-1);quickSort($arr,$bigger+1,$end);}可见该方法并没有申请新的临时数组,空间复杂度为O(1),但效率不如该方法和方法一一样,测试结果的执行时间是方法一的近一倍,显然做的不够好。仔细查看执行过程,发现有时右边大于参考值的元素也会被交换。向左,然后交换回来,冗余操作。第三种方法仍然是使用元素交换法,但是每次交换只是将左边大于参考值的元素和右边小于参考值的元素进行交换。函数quickSortOptimize(&$arr,$start,$end){如果($start>=$end)返回;$smaller=$start;$更大=$结束;$temp=$arr[$smaller];while($smaller<$bigger){//从右边开始,找一个比参考值小的元素,赋给smaller,然后把值留出来while($smaller<$bigger&&$arr[$bigger]>=$temp)$更大--;$arr[$smaller]=$arr[$bigger];//然后从左边找一个大于参考值的元素赋值给空的$biggerwhile($smaller<$bigger&&$arr[$smaller]<$temp)$smaller++;$arr[$bigger]=$arr[$smaller];}$arr[$smaller]=$temp;quickSortOptimize($arr,$start,$smaller-1);quickSortOptimize($arr,$bigger+1,$end);}以上三个方法都是递归实现的,那么有没有非递归的方式呢?当然有。我们把每次要排序的数组段,丢到一个临时数组里,一个一个的取出来排序。在方法三的基础上,我们稍作改动。函数quickSortStack(&$arr,$start,$end){$stack[]=[$start,$end];while(!empty($stack)){list($start,$end)=array_pop($stack);$smaller=$start;$更大=$结束;如果($start>=$end)继续;$temp=$arr[$smaller];while($smaller<$bigger){//从右边搜索一个小于参考值的元素,赋值给smaller,然后清空值while($smaller<$bigger&&$arr[$bigger]>=$temp)$更大--;$arr[$smaller]=$arr[$bigger];//然后从左边找一个大于参考值的元素赋值给空的$biggerwhile($smaller<$bigger&&$arr[$smaller]<$temp)$smaller++;$arr[$bigger]=$arr[$smaller];}$arr[$smaller]=$temp;如果($start<$smaller-1)array_push($stack,[$start,$smaller-1]);如果($end>$bigger+1)array_push($stack,[$bigger+1,$end]);}}使用一个临时数组来存储边界值。时间复杂度与方法3完全相同。下面我们分别测试一下这几种方法的效率,以100万个元素为例$arr=range(1,1000000);$sortArr=$arr;洗牌($arr);$tempArr1=$tempArr2=$tempArr3=$arr;echo'--------方法1------------',PHP_EOL;$startTime=microtime(true);quickSortByTempArr($arr);$endTime=微时间(真);echo'sort--',$sortArr==$arr,PHP_EOL;echo'总时间',$endTime-$startTime,PHP_EOL;echo'--------方法2-----------',PHP_EOL;$startTime=microtime(true);quickSort($tempArr1,0,count($tempArr1)-1);$endTime=microtime(true);echo'sort--',$sortArr==$tempArr1,PHP_EOL;echo'总时间',$endTime-$startTime,PHP_EOL;echo'---------方法3------------',PHP_EOL;$startTime=microtime(true);quickSortOptimize($tempArr2,0,count($tempArr2)-1);$endTime=microtime(true);echo'sort--',$sortArr==$tempArr2,PHP_EOL;echo'总时间',$endTime-$startTime,PHP_EOL;echo'--------方法4------------',PHP_EOL;$startTime=microtime(true);quickSortStack($tempArr3,0,count($tempArr3)-1);$endTime=麦克风rotime(true);echo'sort--',$sortArr==$tempArr3,PHP_EOL;echo'totaltime',$endTime-$startTime,PHP_EOL;