一、冒泡排序介绍

冒泡排序是一种基于比较交换操作的排序算法。 每轮冒泡的过程都是从第一个元素开始,将该元素和相邻下一个元素进行比较交换,使得较大的元素向右移动(如果该元素大于下一个元素,则两个元素交换;如果该元素小于等于下一个元素,则保持不变)。这样一来,每轮冒泡的过程都可以确定一个元素放在正确的位置上,而这个元素就是剩余元素中最大的元素,正确的位置就是剩余位置中的最右侧的位置。这个过程就像是气泡上浮一样,所以叫做冒泡排序。

二、冒泡排序图例

下面来看一个冒泡排序的实例,方便理解冒泡的过程(此处以升序为例):

三、冒泡排序实现

1、第一版实现

这是根据前面的过程分析和图例例子直接实现的第一版冒泡排序。

public class BubbleSort {

    private static int number = 0; //记录冒泡排序的轮数

    public static void main(String[] args) {
        int[] array = new int[]{5, 3, 6, 2, 1, 4, 8, 7};

        bubbleSort(array);
        System.out.println(Arrays.toString(array));
        System.out.println("共经过" + number + "轮冒泡排序");
    }

    public static void bubbleSort(int[] array) {
        //外层控制冒泡的轮数,循环次数等于数组元素个数
        for (int i = 0; i < array.length; i++) {
            /*
                内层控制每轮冒泡的比较交换过程:
                    每轮都是从数组第一个元素开始进行比较
                    每轮的比较次数依次递减
             */
            for (int j = 0; j < array.length - i - 1; j++) {
                //比较交换
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
            number++;
        }
    }

}

执行结果如下:

2、优化版本

回顾上面的图例,可以发现实际上第四轮结束后,数组已经是有序的,所以后面的每轮冒泡都只有比较操作而没有交换操作了。这是因为前面的每轮冒泡不仅会确定一个最大的元素放在最右侧,还会使得较大的元素往右侧移动,所以冒泡排序通常会提前几轮就能达到有序(除非数组是完全逆序的,大家可以举个例子思考一下此时需要多少轮排序【轮数=元素个数】)。 优化点1:如果在某轮排序中,没有发生交换操作,说明数组已经有序,可提前退出排序。

优化版本1

public class BubbleSort {

    private static int number = 0; //记录冒泡排序的轮数

    public static void main(String[] args) {
        int[] array = new int[]{5, 3, 6, 2, 1, 4, 8, 7};

        bubbleSort(array);
        System.out.println(Arrays.toString(array));
        System.out.println("共经过" + number + "轮冒泡排序");
    }

    public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            boolean isOccurExchange = false; //表示是否发生交换操作,每轮结束后置为false
            for (int j = 0; j < array.length - i - 1; j++) {
                //比较交换
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    isOccurExchange = true;
                }
            }
            number++;
            if (!isOccurExchange) {//说明这轮没有发生元素交换,数组已经有序
                break;
            }
        }
    }

}

执行结果如下:

接着回顾前面的图例,第二轮结束时,最后发生交换的是5和4,实际上元素5、6、7、8均已有序,可是第三轮比较操作依然进行到元素6;第三轮结束时,最后发生交换的是3和1,此时3、4、5、6、7、8已有序,可是第四轮比较操作依然进行到元素5。所以如果能得知每轮排序过程中最后发生交换操作的位置,下一轮比较操作只需要进行到该位置即可结束。 优化点2:每轮排序过程中,记录下最后发生交换操作的位置,在下一轮循环中只需要比较到该位置即可,如果该位置为0,即数组第一个元素,说明数组已经有序,可提前退出排序。

优化版本2

public class BubbleSort {

    private static int number = 0; //记录冒泡排序的轮数

    public static void main(String[] args) {
        int[] array = new int[]{5, 3, 6, 2, 1, 4, 8, 7};

        bubbleSort(array);
        System.out.println(Arrays.toString(array));
        System.out.println("共经过" + number + "轮冒泡排序");
    }

    public static void bubbleSort(int[] array) {
        int lastExchangeIndex = 0; //记录每轮排序过程中最后发生交换操作的位置
        int unorderedEndIndex = array.length - 1; //记录需要进行比较(无序)范围的最后位置
        for (int i = 0; i < array.length; i++) {
            boolean isOccurExchange = false; //表示是否发生交换操作,每轮结束后置为false
            for (int j = 0; j < unorderedEndIndex; j++) {
                //比较交换
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    isOccurExchange = true;
                    //j后面的元素均已有序,下轮排序只需比较到j位置即可
                    lastExchangeIndex = j;
                }
            }

            unorderedEndIndex = lastExchangeIndex;
            number++;
            if (unorderedEndIndex == 0) {//数组已经有序
                break;
            }
            if (!isOccurExchange) {//说明这轮没有发生元素交换,数组已经有序
                break;
            }
        }
    }

}

执行结果如下:

四、冒泡排序分析

1、冒泡排序是原地排序算法

因为冒泡排序过程中并不需要开辟新的数组空间,只需要常数个变量用于标记或者交换,所以冒泡排序是原地排序算法。

2、冒泡排序是稳定排序算法

在比较交换操作过程中,如果第一个元素大于第二个元素,我们才交换两个元素,两个元素相等时,保持不变。所以两个相等的元素在排序前后的相对位置并不会发生变化,所以冒泡排序是稳定排序算法。

3、时间复杂度是O(n2)

最好情况

此时数组本身已经有序,冒泡排序只需要一轮就可退出,时间复杂度为O(n)

最坏情况

此时数组本身是逆序的,完成冒泡排序需要n轮,比较的次数为n+(n-1)+(n-2)+...+2+1,时间复杂度为O(n2)

平均情况

鉴于对最坏情况下的时间复杂度分析,得出平均情况下的时间复杂度为O(n2)

原文地址:冒泡排序详解 - 知乎 (zhihu.com)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐