什么是快速排序数组
快速排序是运用分治的方法,经过一趟遍历将要排序的数据分割成独立的两部分,其中一部分的全部数据都比另一部分的全部数据都要小,而后再用一样的方法对这两部分数据分别进行快速排序。 测试
它的流程是这样ui
经常使用实现spa
先来看第一种实现方法,也是比较经常使用的方法,采用临时数组来存储分别存储左边元数和左边元素。咱们选第一个元数为基准值。code
function quickSortByTempArr(&$arr) { $length = count($arr); if ($length <= 1) return; $left = $right = []; for ($i = 1; $i < $length; $i++) { if ($arr[$i] < $arr[0]) { $left[] = $arr[$i]; } else { $right[] = $arr[$i]; } } quickSortByTempArr($left); quickSortByTempArr($right); $arr = array_merge($left, [$arr[0]], $right); }
该方法符合分治的思想,缺点就是申请了临时数组分别存储左右元素,空间复杂度为O(n)blog
再来看方法二,采用元素交换的方式,选取中间元素(或选第一个元素)为基准值,每次比较把更小的元素交换到左边,更大的元素交换到右边。排序
function quickSort(&$arr, $start, $end) { if ($start >= $end) return; $smaller = $start; $bigger = $end; $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; } $smaller++; continue; } // 重复元素 if ($arr[$smaller] == $arr[$middle]) { $middle = $smaller; $smaller++; continue; } // 更大,则跟最右边的未分类数据交换 // 右边全是排好序时, 直接跟middle 交换 if ($bigger <= $middle) { list($arr[$smaller], $arr[$middle]) = [$arr[$middle], $arr[$smaller]]; $middle = $smaller; } else { list($arr[$smaller], $arr[$bigger]) = [$arr[$bigger], $arr[$smaller]]; } $bigger--; } quickSort($arr, $start, $middle - 1); quickSort($arr, $bigger + 1, $end); }
能够看出,该方法并无申请新的临时数组,空间复杂度为 O(1),可是该方法效率不如方法一,测试结果执行时间恒定为方法一的接近2倍,显然不够好。仔细查看执行流程,发现有时会把右侧大于基准值的元素也交换到左侧,而后再交换回去,冗余操做。递归
方法三,仍是采用元素交换的方式,但每次交换都只把左则大于基准值的元素与右侧小于基准值的元素进行交换。it
function quickSortOptimize(&$arr, $start, $end) { if ($start >= $end) return; $smaller = $start; $bigger = $end; $temp = $arr[$smaller]; while ($smaller < $bigger) { // 从右则找一个小于基准值的元素,赋给smaller,而后把该值空出来 while ($smaller < $bigger && $arr[$bigger] >= $temp) $bigger--; $arr[$smaller] = $arr[$bigger]; // 而后从左则找一个大于基准值的元素,赋给空出来的 $bigger while ($smaller < $bigger && $arr[$smaller] < $temp) $smaller++; $arr[$bigger] = $arr[$smaller]; } $arr[$smaller] = $temp; quickSortOptimize($arr, $start, $smaller - 1); quickSortOptimize($arr, $bigger + 1, $end); }
以上三种方法,都是递归的方式实现的,那有没有非递归的方式呢?固然也是有的,咱们把每次待排序的数组分段,丢到一个临时数组里面,依次把它们取出来排序就ok了,在方法三的基础上,稍做改动。io
function quickSortStack(&$arr, $start, $end) { $stack[] = [$start, $end]; while (!empty($stack)) { list($start, $end) = array_pop($stack); $smaller = $start; $bigger = $end; if ($start >= $end) continue; $temp = $arr[$smaller]; while ($smaller < $bigger) { // 从右则找一个小于基准值的元素,赋给smaller,而后把该值空出来 while ($smaller < $bigger && $arr[$bigger] >= $temp) $bigger--; $arr[$smaller] = $arr[$bigger]; // 而后从左则找一个大于基准值的元素,赋给空出来的 $bigger while ($smaller < $bigger && $arr[$smaller] < $temp) $smaller++; $arr[$bigger] = $arr[$smaller]; } $arr[$smaller] = $temp; if ($start < $smaller - 1) array_push($stack, [$start, $smaller - 1]); if ($end > $bigger + 1) array_push($stack, [$bigger + 1, $end]); } }
借助了一个临时数组,来存储边界值。时间复杂度和方法三彻底一致。
接下来咱们分别测试一下这几个方法效率,100万个元素为例
$arr = range(1, 1000000); $sortArr = $arr; shuffle($arr); $tempArr1 = $tempArr2 = $tempArr3 = $arr; echo '---------方法一-----------', PHP_EOL; $startTime = microtime(true); quickSortByTempArr($arr); $endTime = microtime(true); echo 'sort -- ', $sortArr == $arr, PHP_EOL; echo 'total time', $endTime - $startTime, PHP_EOL; echo '---------方法二-----------', PHP_EOL; $startTime = microtime(true); quickSort($tempArr1, 0, count($tempArr1) - 1); $endTime = microtime(true); echo 'sort -- ', $sortArr == $tempArr1, PHP_EOL; echo 'total time', $endTime - $startTime, PHP_EOL; echo '---------方法三-----------', PHP_EOL; $startTime = microtime(true); quickSortOptimize($tempArr2, 0, count($tempArr2) - 1); $endTime = microtime(true); echo 'sort -- ', $sortArr == $tempArr2, PHP_EOL; echo 'total time', $endTime - $startTime, PHP_EOL; echo '---------方法四-----------', PHP_EOL; $startTime = microtime(true); quickSortStack($tempArr3, 0, count($tempArr3) - 1); $endTime = microtime(true); echo 'sort -- ', $sortArr == $tempArr3, PHP_EOL; echo 'total time', $endTime - $startTime, PHP_EOL;