排序算法说明
1 、排序的定义
对一序列对象根据某个关键字进行排序。
2、术语说明
稳定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定 :如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序 :所有排序操作都在内存中完成;
外排序 :由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度 : 一个算法执行所耗费的时间。
空间复杂度 :运行完一个程序所需内存的大小。
3、常见算法总结
image.png
图解说明
n: 数据规模
k: “桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存
4、比较和非比较的区别
十种常见排序算法可以分为两大类:
比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
4.1、常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序 。
在排序的最终结果里,元素之间的次序依赖于它们之间的比较。
每个数都必须和其他数进行比较,才能确定自己的位置 。
比较排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。
在冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n²)。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn)。
4.2、计数排序、基数排序、桶排序则属于非比较排序 。
非比较排序只要确定每个元素之前的已有的元素个数即可,所以一次遍历即可解决。算法时间复杂度O(n)。
非比较排序时间复杂度低,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。
非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr[i]之前有多少个元素,则唯一确定了arr[i]在排序后数组中的位置 。
image.png
5、各个排序法详解
5.1、冒泡排序(Bubble Sort)
冒泡排序是一种简单的排序算法。
算法重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
走访数列的工作重复地进行,直到没有再需要交换,也就是说该数列已经排序完成。
因为排序过程让 较大的数往下沉,较小的往上冒,故而叫冒泡法。
5.1.1 算法描述
1、从第一个元素开始,比较相邻的元素,如果第一个比第二个大,就交换他们两个。
2、从开始第一对到结尾的最后一对,对每一对相邻元素作同样的工作。比较结束后,最后的元素应该会是最大的数。
3、对所有的元素重复以上的步骤,除了最后一个。
4、重复上面的步骤,每次比较的对数会越来越少,直到没有任何一对数字需要比较。
5.1.2 冒泡排序动图演示
5.1.3 示例代码
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
function bubbleSort($arr){
$length = count($arr);
for ($i = 0; $i <$length ; $i++) { // 第一层可以理解为从数组中键为0开始循环到最后一个
for ($j = $i+1; $j < $length; $j++) { // 第二层为从$i+1的地方循环到数组最后
if ($arr[$i] > $arr[$j]) { // 比较数组中两个相邻值的大小
$tem = $arr[$i]; // 这里临时变量,存贮$i的值
$arr[$i] = $arr[$j]; // 第一次更换位置
$arr[$j] = $tem; // 完成位置互换
}
}
}
return $arr;
}
print_r(bubbleSort($arr));
5.1.4算法分析
最佳情况:T(n) = O(n)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
5.2.选择排序(Selection Sort)
表现最稳定的排序算法之一(这个稳定不是指算法层面上的稳定哈,相信聪明的你能明白我说的意思2333),因为无论什么数据进去都是O(n²)的时间复杂度.....所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。
5.2.1算法简介
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
5.2.2算法描述和实现
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
<1>.初始状态:无序区为R[1..n],有序区为空;
<2>.第i趟排序(i=1,2,3...n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
<3>.n-1趟结束,数组有序化了。
5.2.3、代码实现:
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
function SelectionSort($arr)
{
$length = count($arr);
for ($i = 0; $i < $length - 1; $i++) { // $i为已经排序序列的末尾下标
$min = $i; // 暂存未排列序列的最小值下标
for ($j = $i + 1; $j < $length; $j++) { // 遍历未排列序列
if ($arr[$j] < $arr[$min]) { // 找出排列序列最小值,下标赋给$min
$min = $j;
}
}
if ($min != $i) { // 如果找到最小值,放到已排列序列末尾
$t = $arr[$min];
$arr[$min] = $arr[$i];
$arr[$i] = $t;
}
}
return $arr;
}
print_r(SelectionSort($arr));
5.2.4、选择排序动图演示:
5.2.5 算法分析
最佳情况:T(n) = O(n2)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
5.3 、插入排序(Insertion Sort)
插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。当然,如果你说你打扑克牌摸牌的时候从来不按牌的大小整理牌,那估计这辈子你对插入排序的算法都不会产生任何兴趣了.....
5.3.1、算法简介
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
5.3.2、算法描述和实现
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
<1>.从第一个元素开始,该元素可以认为已经被排序;
<2>.取出下一个元素,在已经排序的元素序列中从后向前扫描;
<3>.如果该元素(已排序)大于新元素,将该元素移到下一位置;
<4>.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
<5>.将新元素插入到该位置后;
<6>.重复步骤2~5。
5.3.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
//插入排序
function insert_sort($arr) {
//获取数组单元个数
$count = count($arr);
//外层循环用于从未排序区域中取出待排序元素
for ($i=1; $i < $count; $i++) {
//获取当前需要插入已排序区域的元素值
$temp = $arr[$i];
//内层循环用于从已排序区域寻找待排序元素的插入位置
for ($j=$i-1; $j >= 0; $j--) {
//如果$arr[$i]比已排序区域的$arr[$j]小,就后移$arr[$j]
if ($temp < $arr[$j]) {
$arr[$j+1] = $arr[$j];
$arr[$j] = $temp;
} else {
//如果$arr[$i]不小于$arr[$j],则对已排序区无需再排序
break;
}
}
}
return $arr;
}
print_r(insert_sort($arr));
5.3.4、动图演示:
5.3.5 算法分析
最佳情况:T(n) = O(n)
最坏情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
5.4希尔排序(Shell Sort)
5.4.1、算法简介
希尔排序是希尔(Donald Shell) 于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
5.4.2、算法描述和实现
我们来看下希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2…1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
步骤1:选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
步骤2:按增量序列个数k,对序列进行k 趟排序;
步骤3:每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
5.4.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
//希尔排序(对直接插入排序的改进)
function ShellSort(array $arr)
{
$len = count($arr); // 将$arr按升序排列
$f = 3; // 定义因子
$h = 1; // 最小为1
while ($h < $len/$f){
$h = $f*$h + 1; // 1, 4, 13, 40, 121, 364, 1093, ...
}
while ($h >= 1){ // 将数组变为h有序
for ($i = $h; $i < $len; $i++){ // 将a[i]插入到a[i-h], a[i-2*h], a[i-3*h]... 之中 (算法的关键
for ($j = $i; $j >= $h; $j -= $h){
if ($arr[$j] < $arr[$j-$h]){
$temp = $arr[$j];
$arr[$j] = $arr[$j-$h];
$arr[$j-$h] = $temp;
}
//print_r($arr);echo '<br/>'; // 打开这行注释,可以看到每一步被替换的情形
}
}
$h = intval($h/$f);
}
return $arr;
}
print_r(ShellSort($arr));
5.4.4、动图演示:
5.4.5 算法分析
最佳情况:T(n) = O(nlog2 n)
最坏情况:T(n) = O(nlog2 n)
平均情况:T(n) =O(nlog2n)
5.5、归并排序(Merge Sort)
5.5.1、算法简介
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。
归并排序 是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
5.5.2、算法描述和实现
1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
2、设定两个指针,最初位置分别为两个已经排序序列的起始位置
3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4、重复步骤3直到某一指针达到序列尾
5、将另一序列剩下的所有元素直接复制到合并序列尾
5.5.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
// 归并排序
/**
*递归拆分,直到剩下一个元素,我们认为他是有序的
*/
function merge_sort(array $lists)
{
$n = count($lists);
if ($n <= 1) {
return $lists;
}
$left = merge_sort(array_slice($lists, 0, floor($n / 2)));
$right = merge_sort(array_slice($lists, floor($n / 2)));
$lists = merge($left, $right);
return $lists;
}
/**
*两两循环比较,当左边的第一个元素小于右边的第一个元素,将左边第一个元素放入临时数组
*然后拿左边数组的第二个元素与右边数组的第一个元素比较,如果左边第二个元素大于右边第一个元素,将
*右边第一个放入临时数组
*依次比较,知道有一边的元素取完为止
*/
function merge(array $left, array $right)
{
$lists = [];
$i = $j = 0;
while ($i < count($left) && $j < count($right)) {
if ($left[$i] < $right[$j]) {
$lists[] = $left[$i];
$i++;
} else {
$lists[] = $right[$j];
$j++;
}
}
$lists = array_merge($lists, array_slice($left, $i));
$lists = array_merge($lists, array_slice($right, $j));
return $lists;
}
print_r(merge_sort($arr));
5.5.4、动图演示:
5.5.5 算法分析
最佳情况:T(n) = O(n)
最差情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)
5.6、快速排序(Quick Sort)
5.6.1、算法简介
快速排序 的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
5.6.2、算法描述和实现
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
1、从数列中挑出一个元素,称为 “基准”(pivot);
2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的
数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
5.6.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
//快速排序
function quick_sort(array $arr)
{
// 判断是否需要运行,因下面已拿出一个中间值,这里<=1
if (count($arr) <= 1) {
return $arr;
}
$middle = $arr[0]; // 中间值
//简写法 $left = $right = array();
$left = array(); // 以基准值为分界线,小于基准值的放在左侧
$right = array();// 以基准值为分界线,大于基准值的放在右侧
// 循环比较
for ($i=1; $i < count($arr); $i++) {
if ($middle < $arr[$i]) {
$right[] = $arr[$i]; // 大于中间值
} else {
$left[] = $arr[$i]; // 小于中间值
}
}
// 递归排序划分好的2边
$left = quick_sort($left);
$right = quick_sort($right);
// 合并排序后的数据,别忘了合并中间值
return array_merge($left, array($middle), $right);
}
print_r(quick_sort($arr));
5.6.4、动图演示:
5.6.5 算法分析
最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(nlogn)
5.7、堆排序(Heap Sort)
5.7.1、算法简介
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
5.7.2、算法描述和实现
1、创建一个堆H[0..n-1];
2、把堆首(最大值)和堆尾互换;
3、把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置;
4、 重复步骤2,直到堆的尺寸为1。
5.7.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
//堆排序(对简单选择排序的改进)
/**
* 使用异或交换2个值,原理:一个值经过同一个值的2次异或后,原值不变
* @param int $a
* @param int $b
*/
function swap(array &$arr,$a,$b){
$temp = $arr[$a];
$arr[$a] = $arr[$b];
$arr[$b] = $temp;
}
/**
* 整理当前树节点($n),临界点$last之后为已排序好的元素
* 调整 $arr[$start]的关键字,使$arr[$start]、$arr[$start+1]、、、$arr[$end]成为一个大根堆(根节点最大的完全二叉树)
* 注意这里节点 s 的左右子集是 2*s + 1 和 2*s+2 (数组开始下标为 0 时)
* @param int $n
* @param int $last
* @param array $arr
*
*/
function HeapAdjust(array &$arr,$start,$end){
$temp = $arr[$start];
//沿关键字较大的子集节点向下筛选
//左右子集计算(我这里数组开始下标识 0)
//左子集2 * $start + 1,右子集2 * $start + 2
for($j = 2 * $start + 1;$j <= $end;$j = 2 * $j + 1){
if($j != $end && $arr[$j] < $arr[$j + 1]){
$j ++; //转化为右子集
}
if($temp >= $arr[$j]){
break; //已经满足大根堆
}
//将根节点设置为子节点的较大值
$arr[$start] = $arr[$j];
//继续往下
$start = $j;
}
$arr[$start] = $temp;
}
/**
* 堆排序(最大堆)
* @param array $arr
*/
function HeapSort(array &$arr){
$count = count($arr);
//先将数组构造成大根堆(由于是完全二叉树,所以这里用floor($count/2)-1,下标小于或等于这数的节点都是有子集的节点)
for($i = floor($count / 2) - 1;$i >= 0;$i --){
HeapAdjust($arr,$i,$count);
}
for($i = $count - 1;$i >= 0;$i --){
//将堆顶元素与最后一个元素交换,获取到最大元素(交换后的最后一个元素),将最大元素放到数组末尾
swap($arr,0,$i);
//经过交换,将最后一个元素(最大元素)脱离大根堆,并将未经排序的新树($arr[0...$i-1])重新调整为大根堆
HeapAdjust($arr,0,$i - 1);
}
}
HeapSort($arr);
print_r($arr);
5.7.4、动图演示:
5.7.5 算法分析
最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)
5.8、计数排序(Counting Sort)
5.8.1、算法简介
计数排序 的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
计数排序(Counting sort) 是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。
5.8.2、算法描述和实现
1、找出待排序的数组中最大和最小的元素;
2、统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3、对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
4、反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
5.8.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
/**
* 计数排序: 桶排序的一种
*/
function counting_sort($arr)
{
$min = min($arr);
$max = max($arr);
$count = array();
for($i = $min; $i <= $max; $i++)
{
$count[$i] = 0;
}
foreach($arr as $number)
{
$count[$number]++;
}
$z = 0;
for($i = $min; $i <= $max; $i++) {
while( $count[$i]-- > 0 ) {
$arr[$z++] = $i;
}
}
return $arr;
}
print_r(counting_sort($arr));
5.8.4、动图演示:
5.8.5 算法分析
当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。
最佳情况:T(n) = O(n+k)
最差情况:T(n) = O(n+k)
平均情况:T(n) = O(n+k)
5.9、桶排序(Bucket Sort)
5.9.1、算法简介
桶排序 是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
桶排序 (Bucket sort)的工作的原理:
假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排
5.9.2、算法描述和实现
1、设置一个定量的数组当作空桶;
2、遍历输入数据,并且把数据一个一个放到对应的桶里去;
3、对每个不是空的桶进行排序;
4、从不是空的桶里把排好序的数据拼接起来。
5.9.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
/**
* 桶排序的办法,每个桶存储相同值的数据
* */
function bucketSort($nonSortArray){
//选出桶中最大值和最小值
$min = min($nonSortArray);
$max = max($nonSortArray);
//生成桶,默认每个桶中数据只有0个
$bucket = array_fill($min, $max-$min+1, 0);
//数据入桶
foreach ($nonSortArray as $value){
$bucket[$value]++;//对应桶的个数计增
}
//数据出桶
$sortArray = array();
foreach ($bucket as $k=>$v){
for($i=1;$i<=$v;$i++){
//每个桶中的数据个数
$sortArray[]=$k;
}
}
return $sortArray;
}
print_r(bucketSort($arr));
5.9.4、动图演示:
5.9.5 算法分析
可以看出时间复杂度还是蛮高的,因为要把最小值和最大值之间的数据都要生成数组,所以适合数据密集度比较高的,极差比较小的,而且只能用于整数排序,可见这个应用范围还是挺小的;
说的官方一点就是:
1)待排序列的值处于一个可枚举的范围内
2)待排序列所在可枚举范围不应太大,不然开销会很大
最佳情况:T(n) = O(n+k)
最差情况:T(n) = O(n+k)
平均情况:T(n) = O(n2)
5.10、
5.10.1、算法简介
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
5.10.2、算法描述和实现
1、取得数组中的最大数,并取得位数;
2、arr为原始数组,从最低位开始取每个位组成radix数组;
3、对radix进行计数排序(利用计数排序适用于小范围数的特点);
5.10.3、代码实现:
<?php
$arr = array(23,15,43,25,54,2,6,82,11,5,21,32,65,51,66,21,59,42,95,33,72,15,61);
/**
* 基数排序
*
* @param array $lists
* @return array
*/
function radix_sort(array $lists)
{
$radix = 10;
$max = max($lists);
$k = ceil(log($max, $radix));
if ($max == pow($radix, $k)) {
$k++;
}
for ($i = 1; $i <= $k; $i++) {
$newLists = array_fill(0, $radix, []);
for ($j = 0; $j < count($lists); $j++) {
$key = $lists[$j] / pow($radix, $i - 1) % $radix;
$newLists[$key][] = $lists[$j];
}
$lists = [];
for ($j = 0; $j < $radix; $j++) {
$lists = array_merge($lists, $newLists[$j]);
}
}
return $lists;
}
print_r(radix_sort($arr));
5.10.4、动图演示:
5.10.5、 算法分析
最佳情况:T(n) = O(n * k)
最差情况:T(n) = O(n * k)
平均情况:T(n) = O(n * k)
5.10.6、基数排序有两种方法:
MSD 从高位开始进行排序
LSD 从低位开始进行排序
基数排序 vs 计数排序 vs 桶排序
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
基数排序: 根据键值的每位数字来分配桶
计数排序: 每个桶只存储单一键值
桶排序: 每个桶存储一定范围的数值
d 表示位数,
k 在基数排序中表示 k 进制,
在桶排序中表示桶的个数, maxV 和 minV 表示元素最大值和最小值。
首先,基数排序和计数排序都可以看作是桶排序。
计数排序本质上是一种特殊的桶排序,当桶的个数取最大( maxV-minV+1 )的时候,就变成了计数排序。基数排序也是一种桶排序。
桶排序是按值区间划分桶,基数排序是按数位来划分;基数排序可以看做是多轮桶排序,每个数位上都进行一轮桶排序。当用最大值作为基数时,基数排序就退化成了计数排序。
当使用2进制时, k=2 最小,位数 d 最大,时间复杂度 O(nd) 会变大,空间复杂度 O(n+k) 会变小。当用最大
值作为基数时, k=maxV 最大, d=1 最小,此时时间复杂度 O(nd) 变小,但是空间复杂度 O(n+k) 会急剧增大,此时基数排序退化成了计数排序。
参考链接:
https://blog.csdn.net/weixin_41190227/article/details/86600821
https://www.cnblogs.com/onepixel/p/7674659.html
https://blog.csdn.net/u012786993/article/details/100189067?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.channel_param
https://blog.csdn.net/l754539910/article/details/87635192