冒泡排序
一、冒泡排序的原理
注意:
1、其实原理就是相邻的两两比较,我们一从小到大为列,谁小谁就在前面;
2、比较完了之后互相要交换值,这个时候通过第三个变量进行交换;
3、比如我们现在数组的大小为5,那么我们就要进行4轮的比较,上面粘贴的实例就是大小为4,经过3轮比较。
二、源代码
<?php function bubbleSort($arr) { $length = count($arr); $count1 = 0; $count2 = 0; //$flag = 1; for($i = 0; $i < $length - 1; $i++){ for($j = $length - 1; $j > $i; $j--){ $count1++; $flag = 0; if($arr[$j-1] > $arr[$j]){ $count2++; $temp = $arr[$j-1]; $arr[$j-1] = $arr[$j]; $arr[$j] = $temp; //$flag = 1; } } } echo "一共进行了".$count1.'比较和'.$count2.'次移动'; var_dump($arr); } $arr = [5,2,6,0,3,9,1,7,4,8]; bubbleSort($arr);
三、算法优化
1、设置标志变量,目的在于判断当前有没有值交换,没有就不交换了,就直接下一轮。上面列子中的第二轮完全没有在交换值,这样会影响效率。
<?php function bubbleSort($arr) { $length = count($arr); $count1 = 0; $count2 = 0; $flag = 1; for($i = 0; $i < $length - 1 && $flag; $i++){ for($j = $length - 1; $j > $i; $j--){ $count1++; $flag = 0; if($arr[$j-1] > $arr[$j]){ $count2++; $temp = $arr[$j-1]; $arr[$j-1] = $arr[$j]; $arr[$j] = $temp; $flag = 1; } } } echo "一共进行了".$count1.'比较和'.$count2.'次移动'; var_dump($arr); } $arr = [5,2,6,0,3,9,1,7,4,8]; bubbleSort($arr);
这样的话多余的比较就不在比较了,这样就减少了比较的次数;
四、性能分析
排序类别 | 排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最坏情况 | 最好情况 | ||||
交换排序 | 冒泡排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
快速排序
一、快速排序的实现流程
二、快速排序的工作原理
原理:指针之间的移动,我们选择一个基准值,当前的值比基准值大我们通过指针的移动将它放在最右边否则放在最左边,图解
例如我们的目标数组是
$arr = [5,2,6,0,3,9,1,7,4,8]
选取的基准值是
$low = 0;
$high = 10;
$point = 5;
第一步:我们选出比基准值小的值,所以我们开始寻找比5小的值,从$high开始,从右向左找,不断的递减$high的值,我们找到了下标8比基准值小的值,这个时候我们将数据4移动到下标为0位置,将数据5移动到下标为8的位置,完成了第一次的比较
$low = 0; $high = 8; $point = 5;
第二步:我们选出比基准值大的值,所以我们寻找比5大的值,从$low开始,从左向右寻找,不断递增$low的值,我们找到了下标2比基准值大的值,,这个时候我们将数据为6的下标和数据为5的下标进行互换,也就是下标2和8进行互换;
$low = 3; $high = 8; $point = 5;
后面的操作将执行第一步和第二步,直到不能在分组整个程序终止。
图解
注意:
1、我们不是一步就得到结果,第一轮结束,我们要对第二轮在进行上述过程,最终得到答案。
2、我们在进行比基准值小的时候,是从右向左开始的(数组下标最右边是数组最大的下标),反之从左向右;理解比基准值大的放在最右边,比基准值小的放在最左边;
3、他的扫描过程是最两端向中间进行扫描。
三、源代码
<?php function swap(array &$arr, $a, $b) { $temp = $arr[$a]; $arr[$a] = $arr[$b]; $arr[$b] = $temp; } function Partition(array &$arr, $low, $high) { $point = $arr[$low]; while($low < $high){//从数组的两端交替向中间扫描 while($low < $high && $arr[$high] >= $point){ $high--;//过滤掉比基准值大的(不需要移动) } swap($arr, $low, $high);//遇到比基准值小的放在数组最左边(这个就是需要交换位置的) while($low < $high && $arr[$low] <= $point){ $low++;//过滤掉比基准值小的(不需要移动) } swap($arr, $low, $high);//遇到比基准值大的放在数组的最右边 } return $low; } //快速排序的实现方法 function Qsort(array &$arr, $low, $high) { if($low < $high){ $point = Partition($arr, $low, $high);//计算基准值 Qsort($arr, $low, $point - 1);//对比基准值较小的一部分进行快速排序,也就是移动到数组的左面 Qsort($arr, $point + 1, $high);//对比基准值较大的一部分进行排序,也就是移动到数组的右面 } } //快速排序的主函数 function QuickSort(array &$arr){ $low = 0; $high = count($arr) - 1; Qsort($arr, $low, $high); } $arr = array(9,1,5,8,3,7,4,6,2); QuickSort($arr); var_dump($arr);
四、快速排序的优化
1、优化选取基准点
三数取中法,我们首先选取目标数组的最大下标、最小下标、中间下标的值,然后对他们进行排序找到中间的那个值作为基准值。
优化的代码:
function Partition(array &$arr, $low, $high) { $mind = $low + ($high - $low)/2; if($arr[$low] > $arr[$high]){ swap($arr, $low, $high); } if($arr[$mind] > $arr[$high]){ swap($arr, $mind, $high); } if($arr[$mind] > $arr[$low]){ swap($arr, $mind, $low); } $point = $arr[$low]; while($low < $high){//从数组的两端交替向中间扫描 while($low < $high && $arr[$high] >= $point){ $high--;//过滤掉比基准值大的(不需要移动) } swap($arr, $low, $high);//遇到比基准值小的放在数组最左边(这个就是需要交换位置的) while($low < $high && $arr[$low] <= $point){ $low++;//过滤掉比基准值小的(不需要移动) } swap($arr, $low, $high);//遇到比基准值大的放在数组的最右边 } return $low; }
2、优化不必要的交换
因为指针来回在移动,这样会带来性能的开销
function Partition(array &$arr, $low, $high) { $point = $arr[$low]; while($low < $high){//从数组的两端交替向中间扫描 while($low < $high && $arr[$high] >= $point){ $high--;//过滤掉比基准值大的(不需要移动) } $arr[$low] = $arr[$high]; while($low < $high && $arr[$low] <= $point){ $low++;//过滤掉比基准值小的(不需要移动) } $arr[$high] = $arr[$low]; } $arr[$low] = $point; return $low; }
3、优化小数组的排序方案
当我们排序的数据比较小的时候我们直接用直接插入排序,当数据量大的时候用快速排序是很有效的;这个时候我们要定义数组的大小,数组在多少范围内就用直接插入排序,否则用快速排序。
4、优化递归操作
我们利用伪递归来解决递归的性能,就是减小递归的次数,将递归栈的空间充分利用起来。
//快速排序的实现方法 function Qsort(array &$arr, $low, $high) { if($low < $high){ while ($low < $high) { $point = Partition($arr, $low, $high);//计算基准值 if($point - $low < $high - $point){ Qsort($arr, $low, $point - 1);//对比基准值较小的一部分进行快速排序,也就是移动到数组的左面 $low = $point + 1; //Qsort($arr, $point + 1, $high); }else{ Qsort($arr, $point+1, $high);//对比基准值较小的一部分进行快速排序,也就是移动到数组的左面 $high = $point - 1; //Qsort($arr, $low, $point - 1) } } } }
五、性能分析
排序类别 | 排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最坏情况 | 最好情况 | ||||
交换排序 | 快速排序 | O(n*log2n) | O(n^2) | O(log2n)-O( n ) | 不稳定 |
总结
1、快速排序是冒泡排序的升级;
2、快速排序和冒泡排序都称之为交换类排序,他们都需要数据之间的交换;快速排序是指针之间的交换而冒泡排序是数值之间的交换。