dsfafaadfsa

十大排序算法之快速排序(4)

快速排序及其优化

基础快速排序

/**
 * 基础快速排序
 * @param $sortData
 * @return array
 */
function quickSort($sortData){
    $count = count($sortData);
    if ($count <= 1) return $sortData;
    $middle = selectPivot($sortData);
    $leftArray = [];
    $rightArray = [];
    for ($i = 1; $i < $count; $i++){
        if ($sortData[$i] > $middle){
            $rightArray[] = $sortData[$i];
        } else {
            $leftArray[] = $sortData[$i];
        }
    }

    $leftArray = quickSort($leftArray);
    $leftArray[] = $middle;
    $rightArray = quickSort($rightArray);
    return array_merge($leftArray, $rightArray);
}
/**
 * 方法(1):固定位置
 * 思想:取序列的第一个或最后一个元素作为基准
 * 
 * 基准位1: 
 */
function selectPivot($sortData){
    return $sortData[0];
}

function getSortData($number = 0){
    $sortData = [];
    for ($i = 0; $i < $number; $i++){
        $sortData[] = rand(0, 1000000); 
    }
    return $sortData;
}    
   

$stime = microtime(true); #获取程序开始执行的时间
$data = quickSort(getSortData(10000));
echo implode(',', $data);
$etime = microtime(true); #获取程序执行结束的时间
$total = $etime - $stime;   #计算差值

基准优化方式

对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。

最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列

优化1:随机选取基准

引入的原因:在待排序列是部分有序时,固定选取枢轴使快排效率底下,要缓解这种情况,就引入了随机选取枢轴

思想:取待排序列中任意一个元素作为基准

function selectPivot($sortData){
    return $sortData[rand(0, count($sortData) - 1)];
}
优化2:三数取中(median-of-three)

引入的原因:虽然随机选取枢轴时,减少出现不好分割的几率,但是还是最坏情况下还是O(n^2),要缓解这种情况,就引入了三数取中选取枢轴

分析:最佳的划分是将待排序的序列分成等长的子序列,最佳的状态我们可以使用序列的中间的值,也就是第N/2个数。可是,这很难算出来,并且会明显减慢快速排序的速度。这样的中值的估计可以通过随机选取三个元素并用它们的中值作为枢纽元而得到。事实上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为枢纽元。显然使用三数中值分割法消除了预排序输入的不好情形,并且减少快排大约14%的比较次数

举例:待排序序列为:8 1 4 9 6 3 5 2 7 0

左边为:8,右边为0,中间为6.

我们这里取三个数排序后,中间那个数作为枢轴,则枢轴为6

注意:在选取中轴值时,可以从由左中右三个中选取扩大到五个元素中或者更多元素中选取,一般的,会有(2t+1)平均分区法(median-of-(2t+1),三平均分区法英文为median-of-three)。

具体思想:对待排序序列中low、mid、high三个位置上数据进行排序,取他们中间的那个数据作为枢轴,并用0下标元素存储枢轴。

function selectPivot($sortData){
      $count = count($sortData);
      $first = $sortData[0];
      $end = $sortData[$count - 1];
      $middle = $sortData[intval($count/ 2)];
      if($first < $middle){
          $tmp = $first;
          $first = $middle;
          $middle = $tmp;
      }
      if($first < $end){
          $tmp = $first;
          $first = $end;
          $end = $tmp;
      }
      if($middle < $end){
          $tmp = $middle;
          $middle = $end;
          $end = $tmp;
      }
      
      return $middle;
}

其他优化方式

优化1、当待排序序列的长度分割到一定大小后,使用插入排序。

优化2、在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割

posted @ 2021-03-03 19:41  狩猎者丿七夜  阅读(62)  评论(0编辑  收藏  举报