zoukankan      html  css  js  c++  java
  • 冒泡排序及简单优化

    1、冒泡排序为两个相邻的数据进行对比,然后根据排序规则,进行位置对换
    2、每次循环找出一个数字按照规则排序的位置,最小循环次数为n-1,n为数组长度

    如下为冒泡排序的一个代码实现

    public static int[] bubbleSorted(int[] arr) {
            int len = arr.length;
            int tmp;
            for (int i = 0; i < len - 1; i++) { // 控制大循环次数
                for (int j = 0; j < len - 1 -i; j++) { // 控制每次大循环中相邻数据的大小对比次数,-i为了提高性能,每次对比排除掉已经排序的数据
                    if (arr[j] > arr[j + 1]) {
                        tmp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = tmp;
                    }
                }
            }
    
            return arr;
        } 

    以上代码中,对于大循环,最坏的情况是需要循环n-1次,如对数组{5,4,3,2,1}进行升序排列,则就至少需要n-1 = 4次外层大循环

    每次循环排序结果为:

    [4, 3, 2, 1, 5]
    [3, 2, 1, 4, 5]
    [2, 1, 3, 4, 5]
    [1, 2, 3, 4, 5]

    但我们一般在排序时,很少会有刚好从倒序排序为升序或者从升序排序为倒序,如数组{1,5,3,2,4},此时n-1的每次循环排序结果为:

    [1, 3, 2, 4, 5]
    [1, 2, 3, 4, 5]
    [1, 2, 3, 4, 5]
    [1, 2, 3, 4, 5]

    可以看到,在第二次排序结束后,该数组已经是一个有序数组,后续的操作都是多余的,所以需要对外层大循环做优化,使其在达到有序数列后,就结束循环

    由于冒泡排序,当发现有两个相邻数据与排序规则不符时,即发生交换,那么反过来思考,如果整个循环都没有数据交换,就证明当前数组已经是一个有序数组了,所以可通过添加当前循环是否发生了数据交换标志位进行优化

    public static int[] bubbleSorted(int[] arr) {
            int len = arr.length;
            int tmp;
            for (int i = 0; i < len - 1; i++) { // 控制大循环次数
                boolean isExchange = false; // 默认未发生数据交换,即当前数组已经为有序数组
                for (int j = 0; j < len - 1 - i; j++) { // 控制每次大循环中相邻数据的大小对比次数
                    if (arr[j] > arr[j + 1]) {
                        tmp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = tmp;
                        isExchange = true;
                    }
                }
                if (!isExchange) {
                    // 已经为有序数组,跳出循环
                    break;
                }
                System.out.println(Arrays.toString(arr)); // 测试
            }
    
            return arr;
        } 

    测试打印结果为:

    [1, 3, 2, 4, 5]
    [1, 2, 3, 4, 5]

    此时,我们发现已经少了两次外层循环,且数组已经为有序数组。

    下面看另外一个数组:{ 1, 5, 4, 3, 6, 7, 8 }

    使用上述添加交换标志位排序方法进行测试,输出

    [1, 4, 3, 5, 6, 7, 8]
    [1, 3, 4, 5, 6, 7, 8]

    第一次循环:

    1和5对比,未交换

    5和4对比,交换

    5和3对比,交换

    5和6对比,未交换

    6和7对比,未交换

    7和8对比,未交换

    第一次循环对比结束后的数据:

    [1, 4, 3, 5, 6, 7, 8]

    第二次循环:

    1和4对比,未交换

    4和3对比,交换

    4和5对比,未交换

    5和6对比,未交换

    6和7对比,未交换

    7和8对比,未交换

    第二次循环对比结束后数据:

    [1, 3, 4, 5, 6, 7, 8]

    已经是一个有序数列,在下次循环时,将不会有位置交换,循环结束

    通过以上对比交换情况发现,两次都对5和6,6和7,7和8进行了对比,但并未发生交换,第二次循环中的5和6,6和7,7和8的对比是无意义的,在第一次循环时已经可以知道他们之间不会发生互换,已经是一个有序序列。

    这种方式可以通过添加上次数据交换标志,在下次对比时,只对比到交换标志处为止来优化

    public static int[] bubbleSorted(int[] arr) {
            int len = arr.length;
            int tmp;
            int innerloopEndIndex = arr.length -1;
            for (int i = 0; i < len - 1; i++) { // 控制大循环次数
                int lastExchangeIndex = 0;
                boolean isExchange = false;
                for (int j = 0; j < innerloopEndIndex - i; j++) { // 控制每次大循环中相邻数据的大小对比次数
                    if (arr[j] > arr[j + 1]) {
                        tmp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = tmp;
                        isExchange = true;
                        lastExchangeIndex = j + 1;
                    }
                }
                innerloopEndIndex = lastExchangeIndex;
                if (!isExchange) {
                    break;
                }
            }
    
            return arr;
        } 

    对经典的冒泡排序优化可以分为两部分

    1、外层循环优化:记录交换标志,如果未发生交换,则跳出循环,排序结束

    2、内层循环优化:记录上次排序位置,下次循环对比到该位置

    对于类似{2,3,4,5,6,1}数组,可通过正向和反向双向冒泡来优化

  • 相关阅读:
    vim
    echo
    kill/xkill/killall/pkill/pidof
    ssh
    使用GSON
    使用GSON
    解析JSON
    解析JSON
    Pull解析方式
    Pull解析方式
  • 原文地址:https://www.cnblogs.com/qq931399960/p/10048504.html
Copyright © 2011-2022 走看看