冒泡排序
什么是冒泡排序(Bubble sort)
要想学习排序算法,最好先从冒泡排序开始,这是最基础的排序算法。它是一种基础的交换排序。
排序前的队列:
排序后的队列期望结果:
一、冒泡排序第一版
冒泡排序的思想:把相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换他们的位置;当一个元素小于右侧相邻元素时,位置不变。
1 /** 2 * 冒泡排序的第一版 3 * @param array $arr 4 * @return array 5 */ 6 public function bubbleSort(array $arr): array 7 { 8 $length = count($arr); 9 for ($i = 0; $i < $length - 1; $i++) { 10 for ($j = 0; $j < $length - $i - 1; $j++) { 11 if ($arr[$j] > $arr[$j + 1]) { 12 $temp = $arr[$j]; 13 $arr[$j] = $arr[$j + 1]; 14 $arr[$j + 1] = $temp; 15 } 16 } 17 } 18 return $arr; 19 }
代码非常简单,使用双循环进行排序。外部循环控制所有的回合,内部循环实现每一轮的冒泡处理,先进行元素比较,再进行元素交换。
二、 冒泡排序第二版:提前结束
经过几轮的排序后,有可能整个数列已经是有序的了。但是按照排序算法,仍然要继续执行后面的排序。
在这种情况下,如果能判断出数列已经有序,并做出标记,那么剩下的几轮排序就不必执行了,可以提前结束工作。
优化后的算法,这里用PHP代码实现如下:
1 /** 2 * 冒泡排序的第二版 3 * @param array $arr 4 * @return array 5 */ 6 public function bubbleSort(array $arr): array 7 { 8 $length = count($arr); 9 10 for ($i = 0; $i < $length - 1; $i++) { 11 // 是否排序标识:如果没有交换,则列表已经完成排序 12 $isSorted = false; 13 for ($j = 0; $j < $length - $i - 1; $j++) { 14 if ($arr[$j] > $arr[$j + 1]) { 15 $temp = $arr[$j]; 16 $arr[$j] = $arr[$j + 1]; 17 $arr[$j + 1] = $temp; 18 $isSorted = true; 19 } 20 } 21 22 if (!$isSorted) { 23 break; 24 } 25 } 26 return $arr; 27 }
三、 冒泡排序第三版:数列有序区的界定
比如上图中这个数列:前半部分的元素(3,4,2,1)无序,后半部分的元素(5,6,7,8)按照升序排列,并且后半部分元素中的最小值也大于前半部分元素的最大值

第二轮排序后的结果:
1)右面的许多元素已经是有序的了,可是每一轮还是白白地比较了许多次。这正是冒泡排序中另一个需要优化的点。
2)这个问题的关键点在于对数列有序区的界定。按照现有的逻辑,有序区的长度和排序的轮数是相等的。例如第一轮排序过后的有序区长度是1,第2轮排序过后的有序长度是2......
实际上,数列真正的有序区可能会大于这个长度,如上图第二轮排序后,后面的5个元素实际上都属于有序区了。因此后面的多次元素比较是没有意义的。那么,该如何避免这种情况呢?
优化方案:我们可以在每一轮排序后,记录下来最后一次元素交换的位置,该位置即为无序数列的边界,再往后就是有序区了。
优化的算法,这里用PHP代码实现如下:
1 /** 2 * 冒泡排序的第三种写法 3 * @param array $arr 4 * @return array 5 */ 6 public function bubbleSort(array $arr): array 7 { 8 $length = count($arr); 9 10 // 记录每一轮最后一次交换的下标 11 $lastExchangeIndex = 0; 12 13 // 无序数列的边界,每次比较只需要比到这里为止 14 $sortBorder = $length - 1; 15 16 for ($i = 0; $i < $length - 1; $i++) { 17 // 是否排序标识:如果没有交换,则列表已经完成排序 18 $isSorted = false; 19 20 for ($j = 0; $j < $sortBorder; $j++) { 21 if ($arr[$j] > $arr[$j + 1]) { 22 $temp = $arr[$j]; 23 $arr[$j] = $arr[$j + 1]; 24 $arr[$j + 1] = $temp; 25 $isSorted = true; 26 27 // 更新为最后一次交换元素的位置 28 $lastExchangeIndex = $j; 29 } 30 } 31 32 // 更新无序数列的边界 33 $sortBorder = $lastExchangeIndex; 34 35 if (!$isSorted) { 36 break; 37 } 38 } 39 return $arr; 40 } 41 }
说明:这里使用两个变量lastExchangeIndex和sortBorder,可以让逻辑更加清晰。当然,从实现效果来说,这里也可以只用一个变量sortBorder,初始值为$length-1,每次内循环中交换元素后重新赋值为$j。这样实现也可以的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2019-04-30 PHP中filesystem的使用