線性表(即線性數據結構,如數組和鏈表)的常規排序算法,包括冒泡、插入、選擇、歸並和快排,其中綜合性能最好的就是快排(快速排序),所以快排在工程實踐中也有大量的應用,比如很多編程語言都提供了排序函數,而這些排序函數基本都是基於快速排序實現的,比如 PHP 的數組排序函數 sort 就是如此。
今天我們將以此函數的底層實現為例,為大家展示如何基於快速排序來實現 PHP 的 sort 函數(准確的說,是綜合運用了插入排序和快速排序)。
PHP 數組排序函數 sort 底層實現分析
首先我們來給大家介紹下 sort 函數,該函數的函數簽名是這樣的:
bool sort ( array &$array [, int $sort_flags = SORT_REGULAR ] )
我們在調用該函數時,需要傳遞一個數組作為第一個參數,並且該參數是一個引用參數,不需要設置返回值,排序結果直接作用在數組本身上。該函數默認對數組內數據進行升序排序,支持對數字和字符串進行排序,更多細節可以參考官方文檔:https://secure.php.net/manual/zh/function.sort.php。
由於 sort 函數的實現隱藏在 PHP 底層核心代碼中,所以首先我們要把 PHP 源碼下載到本地,PHP 底層源碼已經開源到 GitHub 上:php/php-src,你可以自行將其下載到本地以方便查看,PHP 源碼基於 C 語言編寫,所以你需要一個支持 C/C++ 語言解析的編輯器來查看 PHP 源碼,比如 Visual Studio Code、Visual C++、CLion 等。
PHP 源碼中 sort 函數定義位於頭文件 ext/standard/php_array.h 中(其實數組相關函數都定義在這里)

真正的函數實現則位於源碼 ext/standard/array.c 中:


下面我就來分析這段源碼的具體實現:
先對傳入參數進行必要的檢驗和轉換;
再通過 php_get_data_compare_func 設置比較函數(想想 PHP 某些數組排序函數支持自定義排序函數,其實所有排序函數的實現原理都是一樣的,只不過這里指定了一個默認實現);
最后通過 zend_hash_sort 進行排序操作。
如果你對 C 語言不熟,還看不懂具體的代碼實現,沒關系,你只需要知道這個大致的流程就好了,我們重點關注最核心的步驟 zend_hash_sort 的內部實現。
我們追溯 zend_hash_sort 函數的定義,可以看到該函數定義在 Zend/zend_hash.h 頭文件中:
#define zend_hash_sort(ht, compare_func, renumber) \
zend_hash_sort_ex(ht, zend_sort, compare_func, renumber)
這是一個宏定義,你可以將其看作一個別名,當調用 zend_hash_sort(ht, compare_func, renumber) 時實際調用的是 zend_hash_sort_ex(ht, zend_sort, compare_func, renumber) 函數,這里我們可以看到兩個函數對比后者新增了一個參數 zend_sort,該參數是一個函數指針,指向了真正的排序實現函數,位於 Zend/zend_sort.c 中的 zend_sort 函數:

查看 zend_sort 函數的源碼,可以看到當數據量較小時(小於等於16),會使用插入排序,因為此時插入排序性能更好;否則會使用快速排序。
感興趣的同學,可以以此為例查看其他數組排序函數的底層實現,如 asort、ksort、usort 等。
