<?php
/**
* Created by PhpStorm.
* User: air
* Date: 18-8-25
* Time: 下午7:23
*/
/**
* 各个排算法,
* 冒泡排序性能是最低的,绝对不能采用,在数据量较大时,选择排序和插入排序都不适合
* 双路快速排序整体最优,三路快速排序针对重复值较多时最适合
* 堆排序:性能比归并排序好,比快速排序差,在数据完全随机的情况下,和三路快速排序性能相当
* Class Sort
*/
class Sort
{
/**
* 选择排序
* @param $arr
* @return array
*/
static public function selectSort($arr){
$n=sizeof($arr);
for ($i=0;$i<$n;$i++){
$index=$i;
for ($j=$i+1;$j<$n;$j++){
if ($arr[$j]<$arr[$index]){
$index=$j;
}
}
if($index!=$i){
self::swap($arr[$i],$arr[$index]);
}
}
return $arr;
}
/**
* 选择排序的一种,每次都做交换,效率大大降低,不推荐
* @param $arr
* @return mixed
*/
static public function selectSort2($arr){
$n=sizeof($arr);
for ($i=0;$i<$n;$i++){
for ($j=$i+1;$j<$n;$j++){
if ($arr[$j]<$arr[$i]){
self::swap($arr[$i],$arr[$j]);
}
}
}
return $arr;
}
/**
* 插入排序
* @param $arr
* @return mixed
*/
static public function insertSort($arr){
$n=sizeof($arr);
for ($i=1;$i<$n;$i++){
$e=$arr[$i];
for ($j=$i;$j>0&&$arr[$j-1]>$e;$j--){
$arr[$j]=$arr[$j-1];
}
$arr[$j]=$e;
}
return $arr;
}
/**
* 冒泡排序
* @param $arr
* @return mixed
*/
static public function bubbleSort($arr){
$n=sizeof($arr);
for ($i=0;$i<$n;$i++){
for ($j=$n-1;$j>$i;--$j){
if($arr[$j]<$arr[$j-1]){
self::swap($arr[$j],$arr[$j-1]);
}
}
}
return $arr;
}
/**
* 冒泡排序(优化时间并不明显,反而有时会怎增加赋值运算时间,针对基本排好序的数组,优化明显(还得看运气),否则用处不大)
* @param $arr
* @return mixed
*/
static public function bubbleSort2($arr){
$n=sizeof($arr);
$lastSwapPosTemp=0;
for ($i=0;$i<$n;$i++){
$lastSwapPos=$lastSwapPosTemp;
for ($j=$n-1;$j>$lastSwapPos;--$j){
if($arr[$j]<$arr[$j-1]){
self::swap($arr[$j],$arr[$j-1]);
$lastSwapPosTemp=$j; // 记录最后一次交换的位置
}
}
}
return $arr;
}
/**
* 归并排序
* @param $arr
* @return mixed
*/
static public function mergeSort($arr){
self::divide($arr,0,sizeof($arr)-1);
return $arr;
}
/**
* 归并排序,自底向上,性能上比自顶向下的归并排序(mergeSort)要差
* @param $arr
* @return mixed
*/
static public function mergeSortBU($arr){
$n=sizeof($arr);
for ($size=1;$size<=$n;$size+=$size){
for ($i=0;$i+$size<$n;$i+=$size*2){
if($arr[$i+$size-1]>$arr[$i+$size]){
self::merge($arr,$i,$i+$size-1,min($i+$size*2-1,$n-1));
}
}
}
return $arr;
}
/**
* 快速排序
* @param $arr
*/
static public function quickSort($arr){
self::quick($arr,0,sizeof($arr)-1);
return $arr;
}
/**
* 双路快速排序,对于数值非常接近的数组非常有效,即使是随机数也是比较快
* @param $arr
*/
static public function quickSort2($arr){
self::quick2($arr,0,sizeof($arr)-1);
return $arr;
}
/**
* 三路快速排序,对于数值非常接近的数组非常有效,即使是随机数也是比较快
* @param $arr
*/
static public function quickSort3($arr){
self::quick3($arr,0,sizeof($arr)-1);
return $arr;
}
/**
* 快速排序(利用了自带函数,性能没有参考意义)
* 利用 php 自带函数 array_merge ,性能更好,但是消耗更多内存,对于大量重复数值的,不宜使用
* @param $arr
* @return array
*/
static public function quickSort4($arr){
if(count($arr)>1){
self::swap($arr[0],$arr[mt_rand(0,count($arr)-1)]);
$k=$arr[0];
$x=array();
$y=array();
$_size=count($arr);
for($i=1;$i<$_size;$i++){
if($arr[$i]<=$k){
$x[]=$arr[$i];
}elseif($arr[$i]>$k){
$y[]=$arr[$i];
}
}
$x=self::quickSort4($x);
$y=self::quickSort4($y);
return array_merge($x,array($k),$y);
}else{
return$arr;
}
}
/**
* 堆排序(小根堆),
* @param $arr
* @return array
*/
static public function heapMinSort($arr){
self::heapMin($arr);
$n=sizeof($arr);
$temp=array();
for ($i=$n-1;$i>=0;$i--){
$temp[]=$arr[0];
self::swap($arr[$i],$arr[0]);
array_pop($arr);
self::heapMinAdjust($arr);
}
return $temp;
}
/**
* 初始化小根堆
* @param $arr
*/
private function heapMin(&$arr){
$n=sizeof($arr);
if($n==1){
return;
}
for ($i=1;$i<$n;$i++){
$j=$i;
while ($j>0&&$arr[$j]<$arr[(int)(($j+1)/2)-1]){
self::swap($arr[$j],$arr[(int)(($j+1)/2)-1]);
$j=(int)(($j+1)/2)-1;
}
}
}
/**
* 取出堆顶元素后调整堆,使之重新成为小根堆
* @param $arr
*/
private function heapMinAdjust(&$arr){
$n=sizeof($arr);
for ($i=0;2*$i+1<$n;){
if(2*$i+2<$n){ // 左右孩子都有
if($arr[$i]<$arr[2*$i+1]&&$arr[$i]<$arr[2*$i+2]){
break;
}
if ($arr[2*$i+1]<$arr[2*$i+2]){
if($arr[$i]<$arr[2*$i+1]){
break;
}else{
self::swap($arr[$i],$arr[2*$i+1]);
$i=2*$i+1;
}
}else{
if($arr[$i]<$arr[2*$i+2]){
break;
}else{
self::swap($arr[$i],$arr[2*$i+2]);
$i=2*$i+2;
}
}
}else{ // 只有左孩子
if($arr[$i]<$arr[2*$i+1]){
break;
}else{
self::swap($arr[$i],$arr[2*$i+1]);
$i=2*$i+1;
}
}
}
}
/**
* 快速排序递归
* @param $arr
* @param $l
* @param $r
*/
private function quick(&$arr,$l,$r){
if($l>=$r){
return;
}
$p=self::partition($arr,$l,$r);
self::quick($arr,$l,$p-1);
self::quick($arr,$p+1,$r);
}
/**
* 快速排序递归(双路快速排序),
* @param $arr
* @param $l
* @param $r
*/
private function quick2(&$arr,$l,$r){
if($l>=$r){
return;
}
$p=self::partition2($arr,$l,$r);
self::quick2($arr,$l,$p-1);
self::quick2($arr,$p+1,$r);
}
/**
* 快速排序递归(三路快速排序),在有较多重复数值的情况下最合适,但是在完全随机的情况下,性能比双路快速排序慢
* @param $arr
* @param $l
* @param $r
*/
private function quick3(&$arr,$l,$r){
if($l>=$r){
return;
}
self::swap($arr[$l],$arr[mt_rand($l,$r)]);
$e=$arr[$l];
$lt=$l;
$gt=$r+1;
$i=$l+1;
while ($i<$gt){
if ($arr[$i]<$e){
self::swap($arr[$i++],$arr[++$lt]);
}else if ($arr[$i]>$e){
self::swap($arr[$i],$arr[--$gt]);
}else{
$i++;
}
}
self::swap($arr[$l],$arr[$lt]);
self::quick3($arr,$l,$lt-1);
self::quick3($arr,$gt,$r);
}
/**
* 用于快速排序,找去分界点
* @param $arr
* @param $l
* @param $r
* @return mixed
*/
private function partition(&$arr,$l,$r){
self::swap($arr[$l],$arr[mt_rand($l,$r)]);
$e=$arr[$l];
$j=$l;
for ($i=$l+1;$i<=$r;$i++){
if ($arr[$i]<$e){
self::swap($arr[$i],$arr[++$j]);
}
}
self::swap($arr[$l],$arr[$j]);
return $j;
}
/**
* 用于快速排序,找去分界点(双路快速排序)
* @param $arr
* @param $l
* @param $r
* @return mixed
*/
private function partition2(&$arr,$l,$r){
self::swap($arr[$l],$arr[mt_rand($l,$r)]);
$e=$arr[$l];
$i=$l+1;
$j=$r;
while (true){
while ($i<=$r && $arr[$i]<$e){
$i++;
}
while ($j>=$l+1 && $arr[$j]>$e){
$j--;
}
if($i>$j){
break;
}
self::swap($arr[$i],$arr[$j]);
$i++;
$j--;
}
self::swap($arr[$l],$arr[$j]);
return $j;
}
/**
* 归并排序,分治
* @param $arr
* @param $l
* @param $r
*/
private function divide(&$arr,$l,$r){
if($l>=$r){
return;
}
$mid=(int)(($l+$r)/2);
// 分治过程
self::divide($arr,$l,$mid);
self::divide($arr,$mid+1,$r);
if($arr[$mid]>$arr[$mid+1]){
self::merge($arr,$l,$mid,$r);
}
}
/**
* 归并操作
* @param $arr
* @param $l
* @param $mid
* @param $r
*/
private function merge(&$arr,$l,$mid,$r){
$temp=$arr;
$i=$l;
$j=$mid+1;
for ($k=$l;$k<=$r;$k++){
if($i>$mid){ // 判断左边界
$arr[$k]=$temp[$j-$l];
$j++;
}else if($j>$r){ // 判断右边界
$arr[$k]=$temp[$i-$l];
$i++;
}else if($temp[$i-$l]<$temp[$j-$l]){
$arr[$k]=$temp[$i-$l];
$i++;
}else{
$arr[$k]=$temp[$j-$l];
$j++;
}
}
}
/**
* 交换变量数据
* @param $a
* @param $b
*/
private function swap(&$a,&$b){
$temp=$a;
$a=$b;
$b=$temp;
}
}
php 各种排序算法
最新推荐文章于 2023-04-21 23:37:21 发布