常用的排序演算法(五)--選擇排序以及最佳化(PHP實現)

local0發表於2021-09-09

常用的排序演算法系列


選擇排序以及最佳化

選擇排序也是一種常用的簡單的排序演算法,雖然演算法的複雜度是和冒泡一樣都是O(n^2),但效能還是稍微好一些的,在一些簡單的場景中選擇排序非常好用。

假設當前需要進行排序,選擇排序的核心思路是,找到一個最大或者最小的標記位,將其與當前需要排序的位置交換他們的值,這樣的交換就會比冒泡更加有效率。

圖片描述

舉例說明:
假設當前需要排序的陣列如下:
[ 34, 5, 555,14, 88, 5 ]

首先我們要用一個迴圈來遍歷這個陣列,用來做排序的位置交換。
預設呢,假設當前取到的第一個數字是最小的,我們記錄下編號即可,後面找到真正的最小值編號我們再更新即可。

第一次我們取第一個數 34 作為當前的最小值,記錄下他的 最小值編號 0。

接著,我們要再用一個迴圈,去找陣列後面部分的需要交換的位置(也就是找到真正的最小值編號),當前排序的,是編號0位置,因此我們從編號1開始往後找,如果發現後面有小於等於編號位置的值,我們就更新這個最小值編號

往後找,發現 5 比 34 小,於是最小值編號就變成了1 。
繼續往後找,發現編號5的5等於當前最小值5,於是最小值編號就變成了5 。

最後,我們來判斷一下最小值編號和最開始有沒有變化,如果有變化,說明找到了一個更小的數再後面,那就應該進行交換操作把它弄到前面來。如果沒有變化,就是說後面沒有更小的了,我們不需要交換了。往後開始排下一個位置即可。

此時最小值編號是5,已經不等於最開始我需要排序的位置0了,所以我們進行一個交換,得到的結果為:

[ 34, 5, 555,14, 88, 5 ] --> 排序一次後–> [ 5, 5, 555,14, 88, 34 ]

完成之後,我們排下一個位置,也就是位置編號1,重複上面的步驟即可。

位置1的5,找不到更小的,不理
[ 5,5, 555,14, 88, 34 ]

位置2的555,與位置5的34交換
[ 5, 5, 555,14, 88, 34 ] --> [ 5, 5, 34 ,14, 88, 555 ]

位置3的14,找不到更小的,不理
[ 5, 5, 34 ,14, 88, 555 ]

位置4的88,找不到更小的,不理
[ 5, 5, 34 ,14, 88, 555 ]

位置5的555,找不到更小的。不理
[ 5, 5, 34 ,14, 88,** 555** ]

這樣我們就完成了排序的過程。寫程式的時候要特別注意這個編號的概念,我們的目的是為了找編號,然後將對應標號的值去做交換,並不是直接去用這個最小值。

PHP實現排序過程如下:

圖片描述

最佳化

可以看出,我們透過找最小值的下標編號,來減少中間不必要的陣列元素交換操作,而我們每次只是標記一個最小的值,透過一個方向來排序。

因為最大最小其實是一個對稱的操作判斷,有沒有方式可以更快完成這個流程呢?

當然是有的,我們可以把原來單路的選擇排序,改成雙路的,每次標記一個最小和一個最大的值,透過兩個方向來排序。

圖片描述

每次排序的進行兩個標記,一個是找出範圍裡的最大值,另一個是找出範圍裡的最小值,相當於是同時在做一個兩個方向的排序,每次排序能穩定的排出來兩個值。

下一次迴圈的時候,縮減查詢的排序範圍,左邊界的值加一,右邊界的值減一,直到相遇。

這裡其實就有一點點快排的味道出來了,快排是有分治思想在裡面的,所以會更加快。

PHP排序過程如下:

圖片描述

程式碼

PHP選擇排序

<?php
/**
 * Created by PhpStorm.
 * User: L
 * Date: 2018-9-27
 * Time: 11:29
 */

$ar = [5, 8, 6, 4, 12, 5, 3, 1, 89, 54];

echo "input: <br/>";
print_r(json_encode($ar));
echo "<br/>";

selection($ar);

echo "result: <br/>";
print_r(json_encode($ar));
echo "<br/>";

/** 選擇排序
 * @param array $ar
 * @param null $len
 */
function selection(array & $ar, $len = null)
{
    if ($len === null) {
        $len = sizeof($ar);
    }

    for ($i = 0; $i < $len - 1; $i++) {
        $min = $i; //consider i as min_index

        for ($j = $i + 1; $j < $len; $j++) {//find min_index
            if($ar[$j]<=$ar[$min]){
                $min = $j;
            }
        }
        //check min_index and swap
        if($min!=$i){//changeSelectionSort.php
            $t = $ar[$i];
            $ar[$i]=$ar[$min];
            $ar[$min] = $t;
        }

        echo "after 1 times: <br/>";
        print_r(json_encode($ar));
        echo "<br/>";
    }
}

PHP選擇排序-雙路最佳化

<?php
/**
 * Created by PhpStorm.
 * User: L
 * Date: 2018-9-27
 * Time: 11:29
 */

$ar = [5, 8, 6, 4, 12, 5, 3, 1, 89, 54];

echo "input: <br/>";
print_r(json_encode($ar));
echo "<br/>";

selection($ar);

echo "result: <br/>";
print_r(json_encode($ar));
echo "<br/>";

/** 最佳化的選擇排序
 * @param array $ar
 * @param null $len
 */
function selection(array & $ar, $len = null)
{
    if ($len === null) {
        $len = sizeof($ar);
    }


    for ($left = 0, $right = $len - 1; $left < $right; $left++, $right--) {
        $min = $left; //consider left as min_index and max_index
        $max = $right;

        for ($j = $left + 1; $j <= $right; $j++) {//find min_index max_index

            if ($ar[$j] <= $ar[$min]) {
                $min = $j;
            }

            if ($ar[$j] >= $ar[$max]) {
                $max = $j;
            }

        }
        //check min_index and swap
        if ($min != $left) {
            $t = $ar[$left];
            $ar[$left] = $ar[$min];
            $ar[$min] = $t;
        }

        if ($max != $right) {
            $t = $ar[$right];
            $ar[$right] = $ar[$max];
            $ar[$max] = $t;
        }


        echo "after 1 times: <br/>";
        print_r(json_encode($ar));
        echo "<br/>";
    }

}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/132/viewspace-2818162/,如需轉載,請註明出處,否則將追究法律責任。

相關文章