zoukankan      html  css  js  c++  java
  • 交换排序算法之冒泡排序


    八种排序算法可以按照如图分类,本文主要介绍冒泡排序。


    交换排序

    所谓交换,就是序列中任意两个元素进行比较,根据比较结果来交换各自在序列中的位置,以此达到排序的目的。


    冒泡排序

    冒泡排序是一种简单的交换排序算法,以升序排序为例,其核心思想是:

    1. 从第一个元素开始,比较相邻的两个元素。如果第一个比第二个大,则进行交换。
    2. 轮到下一组相邻元素,执行同样的比较操作,再找下一组,直到没有相邻元素可比较为止,此时最后的元素应是最大的数。
    3. 除了每次排序得到的最后一个元素,对剩余元素重复以上步骤,直到没有任何一对元素需要比较为止。

    用 Java 实现的冒泡排序如下

    /**
     * 冒泡排序常规版
     *
     * <p>没有经过任何优化操作的版本
     * @param arr 待排序的整型数组
     * @return 比较次数
     */
    public static int bubbleSortOpt1(int[] arr) {
    
        if (arr == null) {
            throw new NullPointerException();
        } else if (arr.length < 2) {
            return 0;
        }
    
        int temp;
        int count = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
                count++;
            }
        }
        return count;
    }
    

    算法优化

    现在有个问题,假如待排序数组是 2、1、3、4、5 这样的情况,按照上述代码实现,第一次循环即可得出正确结果。但循环并不会停止,而是继续执行,直到结束为止。显然,之后的循环遍历是没有必要的。

    为了解决这个问题,我们可以设置一个标志位,用来表示当前次循环是否有交换,如果没有,则说明当前数组已经完全排序。

    /**
     * 冒泡排序优化第一版
     *
     * <p>设置了一个标志位,用来表示当前次循环是否有交换,如果没有,则说明当前数组已经完全排序
     * @param arr 待排序的整型数组
     * @return 比较次数
     */
    public static int bubbleSortOpt2(int[] arr) {
    
        if (arr == null) {
            throw new NullPointerException();
        } else if (arr.length < 2) {
            return 0;
        }
    
        int temp;
        int count = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            int flag = 1;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = 0;
                }
                count++;
            }
            // 没有发生交换,排序已经完成
            if (flag == 1) {
                return count;
            }
        }
        return count;
    }
    

    算法还可以再优化,比如 3、4、2、1、6、7、8 这个数组,第一次循环后,变为 3、2、1、4、6、7、8 的顺序,我们发现,1 之后的 4 、6、7、8 已经有序了,第二次循环就没必要对后面这段再遍历比较。

    假设一次循环后数组第 i 个元素后所有元素已经有序,优化目标就是下次循环不再花费时间遍历已经有序的部分。关键在于如何定位 i 这个分界点,其实并不难,可以想象,由于 i 之前的元素是无序的,所以一定有交换发生,而 i 之后的元素已经有序,不会发生交换,最后发生交换的地点,就是我们要找的分界点。

    /**
     * 冒泡排序优化第二版
     *
     * <p>定位最后发生交换的分界点,之后的元素无需遍历。
     * @param arr 待排序的整型数组
     * @return 比较次数
     */
    public static int bubbleSortOpt3(int[] arr) {
    
        if (arr == null) {
            throw new RuntimeException();
        } else if (arr.length < 2) {
            return 0;
        }
    
        int temp;
        int count = 0;
        int len = arr.length - 1;
        for (int i = 0; i < len; i++) {
            // 记录最后一次交换位置
            int lastChange = 0;
            for (int j = 0; j < len; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    // 每交换一次更新一次
                    lastChange = j;
                }
                count++;
            }
            // 没有发生交换,排序已经完成
            if (lastChange == 0) {
                return count;
            }
            len = lastChange;
        }
        return count;
    }
    

    我们同样可以用 lastChange 来判断当前次循环是否发生交换,这样就结合了第一种优化方案了。各位可以自行设计测试数据,看看返回的比较次数大小是否减少。


    性能分析

    冒泡排序算法是稳定的,因为相同元素的前后顺序并不会变。

    分析最优版本算法,最好的情况是一开始已经排好序,那就没必要交换了,直接返回,此时只走了外层的第一次循环,最优时间复杂度为 O(n)。

    最坏的情况当然是数组是逆序的,这样里外两层循环都走了遍,最坏时间复杂度为 O( n^2 )。

    空间复杂度则是用于交换的临时变量,至于标志位和其他的因算法的不同实现有差异。


  • 相关阅读:
    LeetCode——Generate Parentheses
    LeetCode——Best Time to Buy and Sell Stock IV
    LeetCode——Best Time to Buy and Sell Stock III
    LeetCode——Best Time to Buy and Sell Stock
    LeetCode——Find Minimum in Rotated Sorted Array
    Mahout实现基于用户的协同过滤算法
    使用Java对文件进行解压缩
    LeetCode——Convert Sorted Array to Binary Search Tree
    LeetCode——Missing Number
    LeetCode——Integer to Roman
  • 原文地址:https://www.cnblogs.com/Yee-Q/p/13384855.html
Copyright © 2011-2022 走看看