首页 > 技术文章 > 十大经典排序算法 ( 一 ) 冒泡排序

JiHC 2020-08-25 17:22 原文

介绍 :
  冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
  它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
  这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

算法原理 :

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

动图演示 :

算法稳定性 :

  冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

时间复杂度 :

  1.如果我们的数据正序,只需要走一趟即可完成排序。所需的比较次数 C 和记录移动次数 M 均达到最小值,即:Cmin=n-1 ; Mmin=0 ; 所以,冒泡排序最好的时间复杂度为 O(n) 。

  2.如果很不幸我们的数据是反序的,则需要进行 n-1 趟排序。每趟排序要进行 n-i 次比较 ( 1 ≤ i ≤ n-1 ),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:

  

  综上所述:冒泡排序总的平均时间复杂度为:O(n2) ,时间复杂度和数据状况无关。

Java代码 :

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Arrays;

/**
 * 冒泡排序
 */
public class BubbleSort {
    private static Log log = LogFactory.getLog (BubbleSort.class);

    public static void main(String[] args) {
        int size = 10;// 数组长度(0 - 10)
        int value = 100;// 值大小(-100 - 100)
        int[] arr = generateRandomArray (size, value);
        // 对象拷贝:一维数组:深克隆(重新分配空间,并将元素复制过去) 
        int[] bubbleArr = arr.clone ();
        int[] sortArr = arr.clone ();

        StringBuilder arrStr = new StringBuilder ("原数组: ");
        for (int number : arr) {
            arrStr.append (number).append (" ");
        }
        log.info (arrStr);

        bubbleSort (bubbleArr);
        StringBuilder bubbleStr = new StringBuilder ("冒泡排序:");
        for (int bValue : bubbleArr) {
            bubbleStr.append (bValue).append (" ");
        }
        log.info (bubbleStr);

        // 绝对正确的方法,这个方法可以时间复杂度很差,但是要保证其准确性
        Arrays.sort (sortArr);// 调用系统的函数来进行验证
        StringBuilder sortStr = new StringBuilder ("系统函数排序: ");
        for (int sValue : sortArr) {
            sortStr.append (sValue).append (" ");
        }
        log.info (sortStr);

        log.info (isEqual (bubbleArr, sortArr) ? "成功" : "失败");
    }

    private static boolean isEqual(int[] arr1, int[] arr2) {
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }

    //N个数字冒泡排序,总共要进行N-1趟比较,每趟的排序次数为(N-i)次比较
    private static void bubbleSort(int[] arr) {
        //判断边界条件
        if (arr == null || arr.length < 2) {
            return;
        }
        log.info ("数组长度: " + arr.length + ", 预计执行" + (arr.length - 1) + "轮排序");
        boolean flag = false;// 判断是否发生交换
        for (int i = 0; i < arr.length - 1; i++) {
            log.info ("=========第" + (i+1) + "轮比较");
            for (int j = 0; j < arr.length - i - 1; j++) {
                // 开始进行比较,如果arr[j]比arr[j+1]的值大,那就交换位置
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = true;// 发生交换
                }
                StringBuilder sb = new StringBuilder ();
                sb.append ("第").append (j+1).append ("次比较:");
                for (int number : arr) {
                    sb.append (number).append (" ");
                }
                log.info (sb);
            }
            if (!flag) {
                log.info ("flag为false,未发生交换,数组有序,退出冒泡排序");
                break;
            }
            flag = false;
        }
    }

    //生成一个对数器。产生一个随机样本的数组,数组的长度和值都是随机的
    private static int[] generateRandomArray(int size, int value) {
        // 生成长度随机的数组,数组的最大长度是size的长度
        int[] arr = new int[(int) ((size + 1) * Math.random ())];
        for (int i = 0; i < arr.length; i++) {
            //针对数组中的每个值都可以随机一下,一个随机数减去另外一个随机数,可能产生正数,也可能产生负数
            arr[i] = (int) ((value + 1) * Math.random ()) - (int) (value * Math.random ());
        }
        return arr;
    }

}

 

推荐阅读