PHP实现快排算法——>递归VS非递归

在这里插入图片描述
算法思路分析:
快速排序的思想就是,选一个数作为基数(这里我选的是第一个数),大于这个基数的放到右边,小于这个基数的放到左边,等于这个基数的数可以放到左边或右边,看自己习惯,这里我是放到了左边,一趟结束后,将基数放到中间分隔的位置,第二趟将数组从基数的位置分成两半,分割后的两个的数组继续重复以上步骤,选基数,将小数放在基数左边,将大数放到基数的右边,在分割数组,直到数组不能再分为止,排序结束。
例如从小到大排序:
1>第一趟,第一个数为基数temp,设置两个指针left = 0,right = n.length,
①从right开始与基数temp比较,如果n[right]>基数temp,则right指针向前移一位,继续与基数temp比较,直到不满足n[right]>基数temp
  ②将n[right]赋给n[left]
  ③从left开始与基数temp比较,如果n[left]<=基数temp,则left指针向后移一位,继续与基数temp比较,直到不满足n[left]<=基数temp
  ④将n[left]赋给n[rigth]
  ⑤重复①-④步,直到left==right结束,将基数temp赋给n[left]  
2> 第二趟,将数组从中间分隔,每个数组再进行第1步的操作,然后再将分隔后的数组进行分隔再快排,
3>递归重复分隔快排,直到数组不能再分,也就是只剩下一个元素的时候,结束递归,排序完成
下面用图,演示第一趟执行流程:
在这里插入图片描述
复杂度分析:
1>时间复杂度:
最坏情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)
这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n。
最好情况下是O(nlog2n),推导过程如下:(递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n) )
在这里插入图片描述
空间复杂度:快速排序使用的空间是O(1)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据:
  最优的情况下空间复杂度为:O(log2n);每一次都平分数组的情况
  最差的情况下空间复杂度为:O( n );退化为冒泡排序的情况
所以平均空间复杂度为O(log2n)。

一、递归实现

<?php
/**
第一种写法
**/
function quickSort($rotateArray, $left, $right)
{
    $temp_left  = $left;
    $temp_right = $right;
    $mid = $rotateArray[intval(($left + $right) / 2)];
    while ($temp_left < $temp_right){                          //左右指针没有重合
        while ($rotateArray[$temp_left] < $mid) ++$temp_left;  //和中间值进行比较,小的指针右移
        while ($rotateArray[$temp_right] > $mid) --$temp_right;//和中间值进行比较,大的指针左移
        if($temp_left <= $temp_right){                         //左边有大于中间值的,右边有小于中间值的
            $temp = $rotateArray[$temp_left];
            $rotateArray[$temp_left] = $rotateArray[$temp_right];
            $rotateArray[$temp_right] = $temp;
            --$temp_right;
            ++$temp_left;
        }
    }
    if($temp_left == $temp_right) $temp_left++;
    if($left < $temp_right) quickSort($rotateArray, $left, $temp_left - 1);
    if($temp_left < $right) quickSort($rotateArray, $temp_right + 1, $right);
}


/**
第二种写法
**/
<?php

function getPosition(&$data, $low, $high) {
    $target = $data[$low];
    //因为交换完后,并没有全部比较完,继续扫描,直到$low = $high 为止
    while ($low < $high) {
        //从右向左找小于target的数
        while ($low < $high && $data[$high] > $target) $high--;
        //找到后交换
        $data[$low] = $data[$high];
        //从左向右找大于target的数
        while ($low < $high && $data[$low] < $target) $low++;
        //找到后交换
        $data[$high] = $data[$low];
    }
    $data[$high] = $target;
    return $high;
}


function quickSort(&$data, $low, $high) {
    $pos = getPosition($data, $low, $high);                         //获取下一次分区位置。
    if ($low < $pos - 1) getPosition($data, $low, $pos - 1);  //如果当前位置没有到达最左侧,往左分区
    if ($pos + 1 < $high) getPosition($data, $pos + 1, $high); //如果当前位置没有到达最右侧,往右分区
}

$data = array(3268);
quickSort($data, 0, count($data) - 1);
var_dump($data);

二、非递归实现

用非递归的算法,主要是利用到栈的思想,递归实现的过程,用数组来保存leftleft 和right两个指针。用pivot = partition(); 实现分区,每次分为左右两个部分(类似于递归实现中的左右递归),然后返回移动后的$left “指针”,用于判断是否需要继续分区。

<?php
/**
 * 快排的非递归实现
 * @param $data
 * @return bool
 */
$data = [];
function quickSort($num)
{
    if(empty($num)) return false;
    global  $data;
    $data = $num;
    $left = 0;
    $right = count($num) - 1;
    $pointer = [];
    array_push($pointer, $left, $right);
    while (! empty($pointer)){
        //先弹出left, right
        $right = array_pop($pointer);     //取数组最后一个元素
        $left    = array_pop($pointer);
        $pivot  = partition($left, $right);
        //先压入$left, 在压入$right
        if($left < $pivot - 1) array_push($pointer, $left, $pivot - 1);
        if($pivot + 1 < $right) array_push($pointer, $pivot + 1, $right);
    }
    return $data;
}
/**
 * 对数组data中下标从left到right的元素,选取基准元素pivot,
 * 根据与基准比较的大小,将各个元素排到基准元素的两端。
 * 返回值为最后基准元素的位置
 */
function partition($left, $right)
{
    global $data;
    $pivot = $data[$left];              //任选一个元素作为枢轴,这里选择第一个元素
    while ($left < $right){
        while ($left < $right && $data[$right] >= $pivot) $right--;
        $data[$left] = $data[$right];
        while ($left < $right && $data[$left] <= $pivot) $left++;
        $data[$right] = $data[$left];
    }
    //当$left = $right
    $data[$left] = $pivot;             //把中间数添加到$low结束的位置
    return $left;
}
var_dump(quickSort([2, 5, 6, 12, 1]));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章