冒泡排序及简单优化

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}数组,可通过正向和反向双向冒泡来优化

 

posted @ 2018-12-01 10:15  大坑水滴  阅读(370)  评论(0编辑  收藏  举报