PHP常用排序算法02——快速、归并
接着上篇,我们继续来学习下工程中最常用的排序算法,适合大规模数据排序的算法,快速排序(quickSort)和归并排序(mergeSort)。
PS:对排序等算法还不太了解的同学,可以去看下这个链接哦,十大经典排序算法(动图演示),一些基础定义和动图演示做的很好啦,代码是用JavaScript实现的。我这个呢是PHP版本,而且有些小细节优化会注明在代码段上,都是经过本人实际操作的,下面开始正文。
1、快速排序
(1)概念:
①选取一个分区点(pivot,随意选,一般选择数组的最后一个元素),将数组分为两部分,左边数组比分区点小,右边数组比分区点大;
②递归重复步骤①,直至最后一次的左右两边分区后只有一个元素,那就是最后排好序的了。
(2)代码实现如下:
//调用快速排序的入口方法
public function index()
{
$arr = [3,8,9,6,5,2,5,5,10,1,66,89,65,45,43,356,56,89];
$n = count($arr) - 1;
$res = static::quickSort($arr,0,$n);
echo '<pre>';
print_r($res);
echo '</pre>';
exit;
}
//快速排序的入口方法
public static function quickSort(&$array,$begin,$end)
{
if($end <= $begin){return;} //递归终止条件
$pivot = static::partition($array,$begin,$end);
static::quickSort($array,$begin,$pivot-1);
static::quickSort($array,$pivot+1,$end);
return $array;
}
//快速排序的分区函数,有两个作用
//一是选取数组一个拆分点,返回拆分点的下标$counter
//二是将数组中小于数组拆分点的值的数放在数组左边,大于拆分点的值的数全放在数组右边
public static function partition(&$arr,$begin,$end)
{
// pivot: 标杆位置,counter: 小于pivot的元素的个数
$pivot = $end;
$counter = $begin;
for($i=$begin;$i<$end;$i++){
if($arr[$i] < $arr[$pivot]){//这里的交换操作是把所有小于pivot的数放在他($counter)前面
$temp = $arr[$counter];
$arr[$counter] = $arr[$i];
$arr[$i] = $temp;
$counter++;
}
}
$temp = $arr[$pivot];//最后counter的位置就是$arr[$pivot]的位置
$arr[$pivot] = $arr[$counter];
$arr[$counter] = $temp;
return $counter;
}
2、归并排序
(1)概念:
①将数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
(2)代码实现:
//调用归并排序的入口方法
public function index()
{
$arr = [3,8,9,6,5,2,5,5,10,1,66,89,65,45,43,356,56,89];
$n = count($arr)-1;
$res = static::mergeSort($arr,0,$n);
echo '<pre>';
print_r($res);
echo '</pre>';
exit;
}
//归并排序的入口方法
public static function mergeSort(&$arr, $start, $end)
{
if($end <= $start){return;}//递归终止条件:只有一个元素
$mid = floor(($start + $end) / 2);
self::mergeSort($arr,$start,$mid);
self::mergeSort($arr,$mid+1,$end);
//拆分完以后需要两两合并
self::merge($arr,$start,$mid,$end);
return $arr;
}
//合并左右两个数组的方法
//主要作用是将左右两个排序好的数组合并在一起,这里需要先使用$tmp这个临时数组来存放,然后在放回$arr
public static function merge(&$arr,$start,$mid,$end){
$i = $start;
$j = $mid+1;
$tmp = [];
//两两循环比较,当左边的第一个元素小于右边的第一个元素,将左边第一个元素放入临时数组
//然后拿左边数组的第二个元素与右边数组的第一个元素比较,如果左边第二个元素大于右边第一个元素,将右边第一个放入临时数组
//依次比较,知道有一边的元素取完为止
while($i<=$mid && $j<=$end){
if($arr[$i] <= $arr[$j]){
$tmp[] = $arr[$i++]; //相当于两条语句:$temp[]=$arr[$i];$i++;
}else{
$tmp[] = $arr[$j++];
}
}
//将左边剩余的元素放入临时数组
while($i<=$mid){
$tmp[] = $arr[$i++];
}
//将右边剩余的元素放入临时数组
while($j<=$end){
$tmp[] = $arr[$j++];
}
//因为前面操作的都是临时数组,而归并操作的事原数组,所以还要把临时数组中的元素放入原数组中
for($k = 0;$k<count($tmp);$k++){
$arr[$k+$start] = $tmp[$k];
}
}
3、快排和归并的比较
(1)归并排序的处理过程是由下到上的,先处理子问题,然后再合并。而快排正好相反,它的处理过程是由上到下的,先分区,然后再处理子问题;
(2)归并排序算法是一种在任何情况下时间复杂度都比较稳定的排序算法,均为O(nlogn),归并排序不是原地排序算法,空间复杂度为O(n),是稳定的排序算法。快速排序算法最坏情况下的时间复杂度是 O(n2),但是平均情况下时间复杂度都是 O(nlogn),快排是原地排序算法,空间复杂度为O(1)。
(3)无论是归并和快速排序都用到了分治思想,递归就是实现分治的一种手段,使用递归的时候我们需要注意两个点,一是递归结束条件,二是递归的公式。使用递归我们必须要有极限的思想,比如说快排,那么他就是拆分到最后,一个数组只有3个值,然后左边是最小的数,右边是最大的数,那么此时就是递归的极限。这个需要自己边敲代码边领会。
PHP常用排序算法02——快速、归并的更多相关文章
- 常用排序算法的python实现和性能分析
常用排序算法的python实现和性能分析 一年一度的换工作高峰又到了,HR大概每天都塞几份简历过来,基本上一天安排两个面试的话,当天就只能加班干活了.趁着面试别人的机会,自己也把一些基础算法和一些面试 ...
- 面试中常用排序算法实现(Java)
当我们进行数据处理的时候,往往需要对数据进行查找操作,一个有序的数据集往往能够在高效的查找算法下快速得到结果.所以排序的效率就会显的十分重要,本篇我们将着重的介绍几个常见的排序算法,涉及如下内容: 排 ...
- Python实现常用排序算法
Python实现常用排序算法 冒泡排序 思路: 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完 ...
- Java常用排序算法及性能测试集合
测试报告: Array length: 20000 bubbleSort : 573 ms bubbleSortAdvanced : 596 ms bubbleSortAdvanced2 : 583 ...
- Java常用排序算法+程序员必须掌握的8大排序算法+二分法查找法
Java 常用排序算法/程序员必须掌握的 8大排序算法 本文由网络资料整理转载而来,如有问题,欢迎指正! 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排 ...
- 转载部长一篇大作:常用排序算法之JavaScript实现
转载部长一篇大作:常用排序算法之JavaScript实现 注:本文是转载实验室同门王部长的大作,找实习找工作在即,本文颇有用处!原文出处:http://www.cnblogs.com/ywang172 ...
- Java 常用排序算法/程序员必须掌握的 8大排序算法
Java 常用排序算法/程序员必须掌握的 8大排序算法 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排序(直接选择排序.堆排序) 4)归并排序 5)分配 ...
- 我们一起来排序——使用Java语言优雅地实现常用排序算法
破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...
- c#实现常用排序算法
让我们先看一看常用排序算法的效率对比 接着请看代码和注释~ using System; using System.Collections.Generic; using System.Linq; usi ...
- C++进阶 STL(3) 第三天 函数对象适配器、常用遍历算法、常用排序算法、常用算数生成算法、常用集合算法、 distance_逆序遍历_修改容器元素
01昨天课程回顾 02函数对象适配器 函数适配器是用来让一个函数对象表现出另外一种类型的函数对象的特征.因为,许多情况下,我们所持有的函数对象或普通函数的参数个数或是返回值类型并不是我们想要的,这时候 ...
随机推荐
- Java 构造函数与修饰符详解:初始化对象与控制权限
Java 构造函数 Java 构造函数 是一种特殊的类方法,用于在创建对象时初始化对象的属性.它与类名相同,并且没有返回值类型. 构造函数的作用: 为对象的属性设置初始值 执行必要的初始化操作 提供创 ...
- 一文告诉你商业智能BI如何推动推动智慧物流发展
随着网络消费的不断发展,推动了物流等相关产业的迅速发展,另外无论是2B业务还是2C业务的生产制造企业,对物流的要求都越来越高,尤其体现在对物流全程透明可视化的需求.商业智能BI平台的引入,大大提高了物 ...
- HarmonyOS Connect FAQ第三期
原文:https://mp.weixin.qq.com/s/YpI9-k4yQvNhaMfg7Li82g,点击链接查看更多技术内容. 在开发HarmonyOS Connect生态产品时,你是否对设 ...
- CentOS下修改 MySQL 的密码
做服务器运维,修改 MySQL 的密码是经常的需要,定期修改 MySQL 密码是网站安全的一个保证.这里记录一下修改 MySQL 密码的一些命令,方便以后查看. 修改root密码 CentOS 下 M ...
- 重学c#系列—— 简单编写一个guid [娱乐篇]
前言 什么是guid? 全局唯一标识符(GUID,Globally Unique Identifier)是一种由算法生成的二进制长度为128位的数字标识符.GUID主要用于在拥有多个节点.多台计算机的 ...
- 《Effective C#》系列之(二)——如何使代码易于阅读和理解
在<Effective C#>这本书中,使代码更易于阅读和理解是其中一章的主要内容.以下是该章节的一些核心建议: 使用清晰.有意义的名称:变量.方法.类型等的名称应该能够准确地描述其含义, ...
- 谷歌浏览器新功能 Copy Declaration
不知道最近大家有没有发现,从谷歌浏览器开发者工具复制 Css 代码到 Vs Code,粘贴之后 Css 代码错乱了.昨天在复制样式代码的时候,突然发现复制的 Css 代码粘贴之后就错乱了.具体表现如下 ...
- HarmonyOS NEXT应用开发—在Native侧实现进度通知功能
介绍 本示例通过模拟下载场景介绍如何将Native的进度信息实时同步到ArkTS侧. 效果图预览 使用说明 点击"Start Download"按钮后,Native侧启动子线程模拟 ...
- 云原生时代如何用 Prometheus 实现性能压测可观测-Metrics 篇
简介:可观测性包括 Metrics.Traces.Logs3 个维度.可观测能力帮助我们在复杂的分布式系统中快速排查.定位问题,是分布式系统中必不可少的运维工具. 作者:拂衣 什么是性能压测可观测 ...
- [FAQ] Solidity 实现倒计时 (count down) ?
思路:一种方式是使用 ethereum-alarm-clock,另一种是合约实现当前过去了多少时间,外部进行不间断调用获得. Any else? Refer:Solidity能倒计时吗 Link:ht ...