冒泡排序

冒泡排序

每一轮循环都会有一个元素被排好序,元素之间进行两两比较。如果大于(按正向前提下),则交换。

冒泡排序是一种很基本的排序算法。基本步骤如下

  • 指向数组中的两个相邻的元素(最开始是数组的头两个元素),比较他们的大小。

    • 【2】【1】【3】【5】 – 将 2 和 1 进行比较
  • 如果顺序错了(即左边的值大于右边),就互换位置。

    • 【1】【2】【3】【5】 – 如果位置是正确的,则什么都不做。
  • 将两个指针向右移动一位

    • 【1】【2】【3】【5】 – 比较 2 和 3 的大小
  • 重复上面的三步,直到数组变有序为止。(上面三步也叫一个轮回)

实战

假设对【4】【2】【7】【1】【3】进行排序。目标是产生一个包含相同元素、升序的数组。

第一轮循环之后:

【2】【4】【1】【3】【[7]】

可以看到,最大的一个数已经冒到了最后一位上。

第二轮循环之后:

【2】【1】【3】【[4]】【[7]】

可以看到,第二大的数已经冒到了倒数第二个位置上。

第三轮循环之后:

【1】【2】【[3]】【[4]】【[7]】

可以看到 3 已经到了最后位置上。(实际上此时的数组已经是有序的了,但是计算机不知道,它仍然会进行下一轮循环)

第四轮循环之后:

【[1]】【[2]】【[3]】【[4]】【[7]】

没有任何交换,此时排序结束。

注意:每轮排序完毕后,已经排好序的元素不需要再参与下一次轮回。因为它已经在正确的位置上了。

Java 代码实现

public static int[] bubbleSort(int[] nums) {
    if(nums == null || nums.length == 1) {
        return nums;
    }
    //用来标记是否已经排序完成
    boolean finish;
    while (true) {
        //每次循环将该值改成 true
        finish = true;
        for(int i = 0; i < nums.length-1; i++) {
            int temp;
            if(nums[i] > nums[i+1]) { //如果已经是有序的了,则不会进入该该判断
                finish = false;
                temp = nums[i];
                nums[i] = nums[i+1];
                nums[i+1] = temp;
            }
        }
        System.out.println("排序结果: " + Arrays.toString(nums));
        if(finish){
            break;
        }
    }
    return nums;
}

这里是简易版本的实现,(这里存在的问题就是每次循环都会将最后一位数字进行比较,实际上不需要,可以使用一个标记位来标记,避免已经确定是正确位置的元素再参与下一论循环)

改进一下:

public static int[] bubbleSortEnhance(int[] nums){
    if(nums == null || nums.length == 1){
        return nums;
    }
    //用来标记是否已经排序完成
    boolean finish;

    //指针,初始为数组最后的索引位
    int pointIndex = nums.length-1;

    while (true){
        //每次循环将该值改成 true
        finish = true;
        System.out.println("本轮该排序的索引位为 [0, " + pointIndex + "]");
        for(int i = 0; i < pointIndex; i++){
            int temp;
            if(nums[i] > nums[i+1]) { //如果已经是有序的了,则不会进入该该判断
                finish = false;
                temp = nums[i];
                nums[i] = nums[i+1];
                nums[i+1] = temp;
            }
        }
        //执行一次,就会有一个元素排好序,然后将 pointIndex--,避免已经排好序的元素进入下一轮的循环
        pointIndex--;
        System.out.println("排序结果: " + Arrays.toString(nums));
        if(finish){
            break;
        }
    }
    return nums;
}

使用 pointIndex 这个值来限制最后参与排序的元素

//输入
private static int[] nums = new int[]{2,3,9,7,5,6,12,10,43, 32};

//输出
本轮该排序的索引位为 [0, 9]
排序结果: [2, 3, 7, 5, 6, 9, 10, 12, 32, 43]
本轮该排序的索引位为 [0, 8]
排序结果: [2, 3, 5, 6, 7, 9, 10, 12, 32, 43]
本轮该排序的索引位为 [0, 7]
排序结果: [2, 3, 5, 6, 7, 9, 10, 12, 32, 43]
排序完毕之后的结果:[2, 3, 5, 6, 7, 9, 10, 12, 32, 43]

时间复杂度

试想一下,对于一个 5 个元素的数组来说,最差的情况下(完全倒序通过冒泡排序变成正序),

  • 第一个元素冒到最后正确的位置上需要执行 4(5-1)次比较
  • 第二个元素冒到最后正确的位置上需要执行 4(3-1)次比较

以此类推,5 个元素(最差的情况下)需要执行 4 + 3 + 2 + 1 = 10 次比较,同样需要执行 4 + 3 + 2 + 1 = 10 次交换。

那么 5 个元素(最差的情况下)需要执行 20 步才能完全排好序。

10 个元素(最差的情况下)需要执行 9 + 8 + 7 + 6 +5 + 4 + 3 + 2 + 1 = 45 次比较,另外还需要 45 次交换,那么就是 90 次才能完全排好序。

那么 n 个元素进行的比较次数就是 (n-1)+ (n-2) + (n-3) + … + 1,交换次数同样也为 (n-1)+ (n-2) + (n-3) + … + 1,两者相加就等于 n2-n

根据这个关系,我们可以类推出以下的结论

元素个数比较交换总次数
5 个元素101020
10 个元素454590
20 个元素190190380
n 个元素 n ( n − 1 ) 2 {n(n-1)\over 2} 2n(n1) n ( n − 1 ) 2 {n(n-1)\over 2} 2n(n1) n 2 − n {n^2-n} n2n

当 n 足够大时,n2 增长的速度远超 n 增长的速度。所以冒泡排序的时间复杂度近似等于 O(N2),也被称之为二次时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值