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]));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章