什么是冒泡排序(Bubble sort)
要想学习排序算法,最好先从冒泡排序开始,这是最基础的排序算法。它是一种基础的交换排序。
排序前的队列:
排序后的队列期望结果:
1、冒泡排序第一版
冒泡排序的思想:把相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换他们的位置;当一个元素小于右侧相邻元素时,位置不变。
冒泡排序是一种稳定排序,值相等的元素并不会打算原本的顺序,由于改排序算法的每一轮都要遍历所有元素,总共遍历(元素数量-1)轮,所以时间复杂度为O(n²)。
这里用PHP代码实现如下:
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 }
代码非常简单,使用双循环进行排序。外部循环控制所有的回合,内部循环实现每一轮的冒泡处理,先进行元素比较,再进行元素交换。
2、 冒泡排序第二版:提前结束
经过几轮的排序后,有可能整个数列已经是有序的了。但是按照排序算法,仍然要继续执行后面的排序。
在这种情况下,如果能判断出数列已经有序,并做出标记,那么剩下的几轮排序就不必执行了,可以提前结束工作。
优化后的算法,这里用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、 冒泡排序第三版:数列有序区的界定
比如上图中这个数列:前半部分的元素(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 }
参考链接:https://visualgo.net/zh/sorting