【PHP數據結構】其它排序:簡單選擇、桶排序

這是我們算法正式文章系列的最後一篇文章了,關於排序的知識我們學習了很多,包括常見的冒泡和快排,也學習過了不太常見的簡單插入和希爾排序。既然今天這是最後一篇文章,也是排序相關的最後一篇,那我們就來輕鬆一些,再來學習兩個非常簡單的排序算法。

簡單選擇排序

首先是簡單選擇排序,它劃分在了選擇類排序下面,不過其實也可以看成是交換類的排序。因爲它的核心代碼中也是有交換操作的實現的。關於這個排序沒有什麼太多好說的,每次在遍歷中找出最大或者最小的數據,然後將它放到相應的位置就可以了。我們先來看代碼,然後再看圖示的解析。

function SelectSort($numbers){
    $n = count($numbers);
    for( $i = 0 ; $i < $n ; $i++){
        $k = $i;
        for( $j = $i+1 ; $j < $n ; $j++){
            if($numbers[$j] < $numbers[$k]){
                $k = $j;
            }
        }
        if($k != $i){
            list($numbers[$i], $numbers[$k]) = [$numbers[$k], $numbers[$i]];
        }
    }
    echo implode(', ', $numbers), PHP_EOL;
}
SelectSort($numbers);
// 13, 27, 38, 49, 49, 65, 76, 97

代碼不復雜吧,可以注意到它也有交換代碼的出現。我們使用的是上篇文章中的小彩蛋中的交換方式進行的數據位置的交換。它和冒泡以及快排那種專門的交換型排序算法還是有些許不同的,每次交換的 i 這個位置是不變的,什麼意思呢?比如我們現在的 i 是 0 ,也就是說整個序列中最小的數據應該是要放在這個地方的。所以 j 循環是從 i + 1 的位置開始循環的,然後不停地和 i 這個位置的數據進行比較,並不斷地更新 k ( k 在一開始是指定爲 i 的)。找到最小的數據之後直接將這個數據和 i 的數據交換,這樣最小的數據就放到了 i 的位置上了。這就是簡單選擇排序的核心思想。

這一大段說起來可能會看得比較懵圈。還是看看圖吧!

我們依然還是以第一趟的詳細過程爲例。

  • k = i ,然後 j 從第二個數據開始遍歷

  • 如果發現 numbers[j] 小於 numbers[k] 的數據,也就是更小的數據,就讓 k = j

  • j 循環遍歷完成後,k 指向的下標就是最小那個數據,於是交換 k 和 i 的值

  • 一趟排序下來,最小的數據就放到了最前面的位置了

是不是感覺和冒泡有點像呀?確實是很像,冒泡也是一趟外層循環就可以把某個最大或者最小的值放到正確的位置上。不過需要注意的是,冒泡是前後兩個數據相比,很有可能每次比較都會發生交換。而選擇排序則是以一個下標指針的位置移動來確定數據,最後也只進行一次交換。所以說,它是有選擇性的交換,而不是純粹的一路交換到底。

簡單桶排序

真正的桶排序還是比較複雜的,但今天我們學習的這個簡單的桶排序則是真的簡單的不行。它體現的是一種以空間換時間的方式,具體是怎麼換的呢?

function BucketSort($numbers){
    $bucketList = [];
    $maxValue = max($numbers);
    for($i=0;$i <= $maxValue;$i++){
        $bucketList[$i] = 0;
    }
    foreach($numbers as $n){
        $bucketList[$n]++;
    }
    $sortList = [];
    foreach($bucketList as $k => $v){
        if($v > 0){
            for( ; $v > 0 ; $v--){
                $sortList[] = $k;
            }
        }
    }
    echo implode(', ', $sortList), PHP_EOL;
}

如果是針對的數字類型的排序操作,特別是這個數字基數不大,比如說是類型枚舉之類的數據,我們都可以使用這種桶排序的方式。首先我們要看當前最大的數字是幾,然後初始化一個數組到這個最大數字的下標,並將所有內容設置爲 0 。接着遍歷原始的排序數組,給這個要排序數據對應的值加 1 。於是,待排序序列所代表的那些鍵的值都會變成 1 ,同時,如果有相同的數據,我們使用的是 ++ 操作,這個數據對應的鍵值就會繼續加 1 。具體的過程就如下圖所示:

相信這個圖已經說明得非常清晰了吧,也不需要我們再深入地解釋了吧。這就是這種最簡單的桶排序方式,我們也可以將這個桶數組的內容換成二維數據,這樣我們就可以實現更復雜數據的排序操作了。不過還是要注意,一定是針對數字類型的哦。我們介紹的這種桶排序其實是真正的桶排序的一種變體,也有人叫它爲“計數排序”。

真正更加完備一些的桶排序其實是先將數據分成不同的組,每個組可以看成是一個桶。然後在這個桶內將組內的數據排序,排序完成之後再將這些組(桶)連接起來。它的時間複雜度是接近於 O(n) 的。不過就像我們介紹的這個最簡單的桶排序一樣,複雜的桶排序也是有許多嚴苛的要求的,所以雖然它的效果很高,但卻並不常見。

總結

今天的內容非常簡單吧,簡單選擇其實也是一種交換排序,但它在大類中還是劃歸到了選擇排序這個類型中。而桶排序是屬於基數排序的一種。各種排序其實還有很多,但除了我們學習的這些之外,其它的都會更加的複雜也並不常見,大家有興趣的可以在下期我們的總結文章中瞭解到有哪些可以繼續深入學習的內容。精彩還在繼續,不要錯過哦!

測試代碼:

https://github.com/zhangyue0503/Data-structure-and-algorithm/blob/master/7.排序/source/7.3其它排序:簡單選擇、桶排序.php

參考文檔:

《數據結構》第二版,嚴蔚敏

《數據結構》第二版,陳越

《啊哈!算法》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章