• 【PHP】冒泡排序以及优化


    最近看了一下冒泡排序这个经典的算法,在网上也看到了很多改进冒泡排序算法的方式,这里总结一下:

    冒泡排序最简单的实现方式如下(我用PHP来实现,用其他语言也是一样的):

    for($i=0;$i<$arr_count;$i++){
        for($j=0;$j<$arr_count-1;$j++){
            if($rand_arr[$j] > $rand_arr[$j+1]){
                //进行交换
                $temp = $rand_arr[$j];
                $rand_arr[$j] = $rand_arr[$j+1];
                $rand_arr[$j+1] = $temp;
            }
        }
    }
    


    例如:[ 3 , 1 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 ]—-针对这个数组我们按照基本冒泡排序算法,需要循环count([ 3 , 1 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 ])轮,也就是10轮。

    第一轮的排序结果如下:[1,3,4,5,6,7,8,9,10,11]

    第二轮的排序结果如下:[1,3,4,5,6,7,8,9,10,11]

    第三轮的排序结果如下:[1,3,4,5,6,7,8,9,10,11]

    第….轮的排序结果如下:[1,3,4,5,6,7,8,9,10,11]

    第十轮的排序结果如下:[1,3,4,5,6,7,8,9,10,11]

    通过上面的介绍,会发现,上面的数组其实在第一轮排序后就已经将数组排序好了。后面的几轮排序其实都是无用功。也就是说下面的代码白白走了9遍,一点作用都没发生。

    上面的白白执行9遍的代码中,每一遍都能在(n-1)的数据比较,如果数组的长度n极大,这个消耗还是很严重的。所以我们首先要解决一下这个问题,怎么才能不造成无谓的数据比较的资源浪费。

    优化策略:在程序中添加标识,用于记录每次外循环过程中,是否发生了数据的交换。如果没有发生数据的交换,就意味者在上一轮的外循环过程中,数据已经排序完成,不需要继续循环比较数据了,我们直接break掉就可以了。如果还存在这数据的交换,就意味着在之前的外部循环中,数据还没有排序好,这一轮排序操作或者接下来的排序操作才可以完成排序工作,我们还需要进行下一轮的排序来完成排序。

    $arr_count = count($rand_arr);
    for($i=0;$i<$arr_count;$i++){
        //设置flag变量,用来记录数据是否交换完成
        $flag = true;
        for($j=0;$j<$arr_count-1;$j++){
            if($rand_arr[$j] > $rand_arr[$j+1]){
                //进行交换
                $temp = $rand_arr[$j];
                $rand_arr[$j] = $rand_arr[$j+1];
                $rand_arr[$j+1] = $temp;
                $flag = false;
            }
        }
        if($flag){
            break;
        }
    }
    

    其实,在进行数据排序的过程中,随着排序的进行,数据逐渐从无序变为有序,出现上面情况的可能性会逐步的增高的。


    好了,说完上面的问题,咱们再来看下面的问题。当冒泡排序的外部循环每执行一次,就会将最大值(最小值)交换到数组的最前端或者数组的最后端,直接看下面的例子。

    经过第一轮排序以后会得到以下的结果:[ 4 , 6 , 2 , 7 , 8 , 0, 9 ] —-也就是说,我们再第一轮求出的最大值9,并且将这个值交换到数组的左部第一的位置上

    经过第二轮排序以后会得到以下的结果:[ 4 , 6 , 2 , 7 , 0 , 8 , 9 ] —-也就是说,我们再第二轮求出的次最大值8,并且将这个值交换到数组的左部第二的位置上

    ……..

    依此类推,我们可以发现,每一轮循环完毕,都能求出某个位置上的应有的数据,并且完成交换。那么也就是说数组的左部(右部)逐渐变得有序,且有序部分的长度等于外部循环进行的轮数。既然数组的左部(右部)逐渐变得有序,那么每次循环过程中,我们就不需要再比较数组的左部(右部)的有序的部分了。

    优化策略:新增一个变量来记录某次外部循环过程中最后发生的数据交换的位置。使用上面记录的位置来作为下一次外部循环中的内部循环的终止条件,这样就能减少不必要数据比较过程。代码如下:

    $arr_count = count($rand_arr);
    $last_pos = $arr_count; //记录每一次外部循环过程中,最后进行数据交换的位置
    $next_pos = $arr_count; //记录每一次数据交换的位置
    for($i=0;$i<$arr_count;$i++){
        for($j=0;$j<$last_pos-1;$j++){
            if($rand_arr[$j] > $rand_arr[$j+1]){
                //进行交换
                $temp = $rand_arr[$j];
                $rand_arr[$j] = $rand_arr[$j+1];
                $rand_arr[$j+1] = $temp;
                $next_pos = $j+1;
            }
       }
       //如果该轮循环得到的最后的数据交换位置等于上一轮循环得到的数据交换的位置,就意味着数组全部有序了
      if($last_pos == $next_pos){
            break;
        }else{//如果没有全部有序,就将last_pos设置为next_pos
            $last_pos = $next_pos;
        }
    }


    解决了上面的问题,我们还能不能进一步的提高冒泡算法的排序速度嘛?答案是可以的。我们可以采用双向排序的方式,进一步加快排序的速度。看下面的实现的例子:

    数组: [ 4 , 3 , 6 , 5 , 9 , 0 , 1 , 7 ]

    双向排序之正向排序(从左到右排序):[ 3 , 4 , 5 , 6 , 0 , 1 , 7 , 9] —计算出数组的最大值9,并将元素移动到数组的最右边

    双向排序之逆向排序(从右到左排序):[ 0 , 3 , 4 , 5 , 6 , 1 , 7 , 9] —计算出数组的最小值0,并将元素移动到数组的最左边

    第一次循环过程,计算出最大值并将该值移动到数组最右边,计算出最小值并将该值移动到数组的最左边。第二次循环过程中,计算出第二大的值并将数据移动到数组右边第二个的位置,计算出第二小的值并将数据移动到数组左边第二个的位置。也会说在一次双向排序循环过程中,求出数组左边以及数组右边各个位置响应的元素,并且移动到响应的位置。

    具体的实现代码如下:

    $arr_count = count($rand_arr);
    $index = 0;     //从左到右排序过程中起始的位置 
    $first_pos = 0; //从右到左排序过程中最后进行数据交换的位置
    $up = $arr_count-1; //从左到右排序过程中结束的位置
    $last_pos = $up; //从左到右排序过程中最后进行数据交换的位置
    while($up >= $index){
        //从头开始扫描
        for($i=$index;$i<$up;$i++){
            if($rand_arr[$i]>$rand_arr[$i+1]){
                //进行交换
                $temp = $rand_arr[$i];
                $rand_arr[$i] = $rand_arr[$i+1];
                $rand_arr[$i+1] = $temp;
                $last_pos = $i;
            }
        }
        if($up == $last_pos){
            break;
        }
        $up = $last_pos;
        //从尾部开始扫描
        for($j=$up;$j>1;$j--){
            if($rand_arr[$j-1]>$rand_arr[$j]){
                //进行交换
                $temp = $rand_arr[$j];
                $rand_arr[$j] = $rand_arr[$j-1];
                $rand_arr[$j-1] = $temp;
                $first_pos = $j;
            }
        }
        if($index == $first_pos){
            break;
        }
        $index = $first_pos;
    }

    网上也有大神对这几种排序做了对比,用了1000个随机数组

    没有进行优化,冒泡执行的时间38.826741201211
    第一次优化后,冒泡执行的时间36.326452970505
    第二次优化后,冒泡执行的时间24.916656017303
    第三次优化后,冒泡执行的时间20.908751010895

  • 相关阅读:
    [Python][小知识][NO.3] Python 使用系统默认浏览器打开指定URL的网址
    [Python][小知识][NO.2] Python 字符串跨行连接,或拆分为多行显示
    [Python] wxPython 状态栏组件、消息对话框组件 学习总结(原创)
    [Python][小知识][NO.1] Python字符串前 加 u、r、b 的含义
    [Python] wxPython 编辑框组件学习总结 (原创)
    [Python] wxPython 菜单栏控件学习总结(原创)
    [Python] wxPython 基本控件 (转)
    HDU 2086 A1 = ? (找规律推导公式 + 水题)(Java版)
    HDU 1840 Equations (简单数学 + 水题)(Java版)
    UVA 1152 4 Values whose Sum is 0 (枚举+中途相遇法)(+Java版)(Java手撕快排+二分)
  • 原文地址:https://www.cnblogs.com/xiangshihua/p/14979544.html
Copyright © 2020-2023  润新知