快速排序是对冒泡排序的一种改进。
快速排序大概是这样子滴:“第一部分的数组,基数的数组,第二部分的数组”组装成的一个数组。
每一次运行方法(调用或递归),小于基数的分为一部分,大于基数的分为另一部分。组装数组的时把基数放中间,就完成了排序。
下面这张图的红色柱子就是基数。它先把小于它的放左半部分,大于它的放右半部分,自己移到中间当中间值。
注意:本文是以低位下标键0作为基数,上图是以最高位键,这是不同的,所以不要把下面的代码和这个图做参考。
你会发现每一波代码的注释都不一样,那是因为整个排序流程我都注释了。
/**
* 快速排序
*
* @param array $arr 数组
* @return array
*/
function quickSort($arr)
{
if (count($arr) > 1) { // 至少需要俩个,才有比对的必要
$base = $arr[0]; // 基数
$left = array(); // 设置一个空数组,等待接收小于基数的值
$right = array(); // 设置一个空数组,等待接收大于基数的值
for ($i = 1; $i < count($arr); $i++) { // 因为下标0已经被基数占用了,所以$i只能从1开始
/**
* 第一轮for循环,如果下标0的基数(1)小于下标1的值(4),条件符合,把下标1的值(4)赋给$right数组
* 第二轮for循环,如果下标0的基数(1)小于下标2的值(3),条件符合,把下标2的值(3)赋给$right数组
* 第三轮for循环,如果下标0的基数(1)小于下标3的值(5),条件符合,把下标3的值(5)赋给$right数组
* 第四轮for循环,如果下标0的基数(1)小于下标4的值(2),条件符合,把下标4的值(2)赋给$right数组
*/
if ($base < $arr[$i]) {
$right[] = $arr[$i];
} else {
$left[] = $arr[$i]; // 把下标1的值(4)赋给$right数组
}
}
$left = quickSort($left); // 因为数组为空,传进去后直接就返回了
$right = quickSort($right); // $right = [4, 3, 5, 2]
return array_merge($left, array($base), $right);
} else {
return $arr;
}
}
$arr = [1, 4, 3, 5, 2];
var_dump(quickSort($arr));
上面的for
运行结果为:$arr = [4, 3, 5, 2];
第一次递归传进方法中
/**
* 第一次递归传进方法中
*
* $arr = [4, 3, 5, 2];
*/
function quickSort($arr)
{
if (count($arr) > 1) {
$base = $arr[0]; // 4
$left = array();
$right = array();
for ($i = 1; $i < count($arr); $i++) {
/**
* 第一轮for循环,如果下标0的基数(4)小于下标1的值(3),条件不符合,进入else,把下标1的值(3)赋给$left数组
* 第二轮for循环,如果下标0的基数(4)小于下标2的值(5),条件符合,把下标2的值(5)赋给$right数组
* 第三轮for循环,如果下标0的基数(4)小于下标3的值(2),条件不符合,进入else,把下标3的值(2)赋给$left数组
*/
if ($base < $arr[$i]) {
$right[] = $arr[$i];
} else {
$left[] = $arr[$i];
}
}
$left = quickSort($left); // $left = [3, 2];
$right = quickSort($right); // 只有一个所以进去了直接返回了
return array_merge($left, array($base), $right);
} else {
return $arr;
}
}
第二次递归传进方法中
/**
* 第二次递归传进方法中
*
* $arr = [3, 2];
*/
function quickSort($arr)
{
if (count($arr) > 1) {
$base = $arr[0]; // 3
$left = array();
$right = array();
for ($i = 1; $i < count($arr); $i++) {
/**
* 第一轮for循环,如果下标0的基数(3)小于下标1的值(2),条件不符合,进入else,把下标1的值(2)赋给$left数组
*/
if ($base < $arr[$i]) {
$right[] = $arr[$i];
} else {
$left[] = $arr[$i];
}
}
$left = quickSort($left); // 只有一个所以进去了直接返回了
$right = quickSort($right); // 为空数组,所以进去了直接返回了
return array_merge($left, array($base), $right); // 返回2与基数3组装的 [2, 3],往下看返回的第一次递归方法中
} else {
return $arr;
}
}
回看第一次递归方法
第二次的递归返回给了第一次递归的方法[2, 3]
,返回给了$left = quickSort($left)
这一行。
而下面还有一行$right = quickSort($right)
没有走完。它只有一个5,所以进去递归直接就返回了。
此时,第一层递归(看下面代码)的这个方法中,$left = [2, 3]
(第二次递归返回的),$base = [4]
(原本就存在于第一次递归方法中),$right = [5]
(看上面那段话),返回[2, 3, 4, 5]
以下代码是上面第一次递归的代码,方便你不用往上拉而已,没有改动。
/**
* 第一次递归传进方法中
*
* $arr = [4, 3, 5, 2];
*/
function quickSort($arr)
{
if (count($arr) > 1) {
$base = $arr[0]; // 4
$left = array();
$right = array();
for ($i = 1; $i < count($arr); $i++) {
/**
* 第一轮for循环,如果下标0的基数(4)小于下标1的值(3),条件不符合,进入else,把下标1的值(3)赋给$left数组
* 第二轮for循环,如果下标0的基数(4)小于下标2的值(5),条件符合,把下标2的值(5)赋给$right数组
* 第三轮for循环,如果下标0的基数(4)小于下标3的值(2),条件不符合,进入else,把下标3的值(2)赋给$left数组
*/
if ($base < $arr[$i]) {
$right[] = $arr[$i];
} else {
$left[] = $arr[$i];
}
}
$left = quickSort($left); // $left = [3, 2];
$right = quickSort($right); // 只有一个所以进去了直接返回了
return array_merge($left, array($base), $right);
} else {
return $arr;
}
}
第一次递归返回了[2, 3, 4, 5]
,其实就是返回给了原方法中的$right
。
此时,原方法(看下面的代码)$left = [] $base = [1] $right = [2, 3, 4, 5]
,组装完成 [1, 2, 3, 4, 5]
以下代码是最上面原方法的代码,方便你不用往上拉而已。
/**
* 快速排序
*
* @param array $arr 数组
* @return array
*/
function quickSort($arr)
{
if (count($arr) > 1) { // 至少需要俩个,才有比对的必要
$base = $arr[0]; // 基数
$left = array(); // 设置一个空数组,等待接收小于基数的值
$right = array(); // 设置一个空数组,等待接收大于基数的值
for ($i = 1; $i < count($arr); $i++) { // 因为下标0已经被基数占用了,所以$i只能从1开始
/**
* 第一轮for循环,如果下标0的基数(1)小于下标1的值(4),条件符合,把下标1的值(4)赋给$right数组
* 第二轮for循环,如果下标0的基数(1)小于下标2的值(3),条件符合,把下标2的值(3)赋给$right数组
* 第三轮for循环,如果下标0的基数(1)小于下标3的值(5),条件符合,把下标3的值(5)赋给$right数组
* 第四轮for循环,如果下标0的基数(1)小于下标4的值(2),条件符合,把下标4的值(2)赋给$right数组
*/
if ($base < $arr[$i]) {
$right[] = $arr[$i];
} else {
$left[] = $arr[$i]; // 把下标1的值(4)赋给$right数组
}
}
$left = quickSort($left); // 因为数组为空,传进去后直接就返回了
$right = quickSort($right); // $right = [4, 3, 5, 2]
return array_merge($left, array($base), $right);
} else {
return $arr;
}
}
你最好自己组装一个乱序的数组,然后重新心读一遍。
总结:
如果要你写,你就贯彻:小于基数的数值,就赋给left,大于就赋给right。