冒泡排序

什么是冒泡排序(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
posted @ 2022-04-30 22:20  欢乐豆123  阅读(135)  评论(0编辑  收藏  举报