排序算法之冒泡排序

原文:微信公众号:程序员小灰——什么是冒泡排序

1 冒泡排序

冒泡排序是一种基于比较的简单的算法,这种算法越大的元素会越来越靠近数列的顶端,就像汽水里面的气泡一样往上冒,因此得名为冒泡排序。

2 原理

首先遍历数组,比较相邻两个元素的大小,如果第一个元素比第二个元素要大则交换两个元素的位置,这样遍历完一轮后最大的元素就到了末尾。重复上面的步骤,只不过因为上一轮遍历后上一轮中最大的元素已经到了末尾,所以不需要比较该元素,即每一次遍历的长度减 1,直到遍历长度为 1 为止。

假如有一个这样的数组:{0, 5, 3, 2, 1, 4, 6, 7, 8, 9},根据上面的思想,它冒泡排序的过程是这样的:

3 代码实现

    public static int[] bubbleSort1(int[] origin) {
        if (origin == null || origin.length == 0) {
            return new int[]{};
        }
        System.out.println("origin--->" + Arrays.toString(origin));
        int index = 0;
        for (int i = 0; i < origin.length - 1; i++) {
            for (int j = 0; j < origin.length - i - 1; j++) {
                if (origin[j] > origin[j + 1]) {
                    int temp = origin[j];
                    origin[j] = origin[j + 1];
                    origin[j + 1] = temp;
                }
                index++;
            }
            System.out.println("第" + (i + 1) + "次比较后" + "origin--->" + Arrays.toString(origin));
        }
        System.out.println("index--->" + index);
        System.out.println("sortedArray--->" + Arrays.toString(origin));
        return origin;
    }

使用测试代码运行一遍:

    @Test
    public void testBubbleSort1() {
//        int[] intArray = new int[10];
//        for (int i = 0; i < intArray.length; i++) {
//            intArray[i] = new Random().nextInt(100);
//        }
         int[] intArray = new int[] { 0, 5, 3, 2, 1, 4, 6, 7, 8, 9 };
        bubbleSort1(intArray);
    }

运行结果如下:

4 优化

从上面的打印中我们可以看出,在第 3 次比较完成后数组其实已经是有序的了,但是后面还进行了 6 次比较,这显然是多余的,所有这些多余的比较我们是可以优化掉一大部分的。在内循环开始前设置一个标志位,如果内循环中有元素的交换,则改变标志位的值,那么如果内循环遍历完标志位都没有改变,则说明已经排序完成,跳出循环,避免掉后面多余的比较。当然,在上面的示例中虽然从第 4 次到第 9 次都是多余的,但是我们得依靠第 4 次的循环来判断是否已经排序完成,所以第 4 次比较是省略不掉的,只能省略第 5 次到第 9 次。

代码如下:

    public static int[] bubbleSort2(int[] origin) {
        if (origin == null || origin.length == 0) {
            return new int[]{};
        }
        System.out.println("origin--->" + Arrays.toString(origin));
        int index = 0;
        for (int i = 0; i < origin.length - 1; i++) {
            // 是否已经排序完成的标志位
            boolean isSorted = true;
            for (int j = 0; j < origin.length - i - 1; j++) {
                if (origin[j] > origin[j + 1]) {
                    int temp = origin[j];
                    origin[j] = origin[j + 1];
                    origin[j + 1] = temp;
                    //如果有交换,标志位置为 false
                    isSorted = false;
                }
                index++;
            }
            System.out.println(
                    "第" + (i + 1) + "次比较后" + "origin--->" + Arrays.toString(origin) + ",isSorted--->" + isSorted);
            //如果内循环结束后标志位仍为 true,则说明已经排序完成,则可以跳出外循环,省略后面多余的比较
            if (isSorted) {
                break;
            }
        }
        System.out.println("index--->" + index);
        System.out.println("sortedArray--->" + Arrays.toString(origin));
        return origin;
    }

打印结果如下,可以看到只有前面 4 次循环,也就是比较 9+8+7+6=30 次。

5 终极优化

同样从上面的打印中我们可以看到,在第 1 次比较后,后面的 4、5、6、7、8、9 就已经是有序的了,后面再来比较这后面的部分它还是不会改变元素的位置,所以第 2 次比较的时候,元素 4,也就是下标为 4 的元素及后面的元素就没有比较的必要了,我们可以记录一下上次比较到哪里(如记录为下标 n)就停止了,也就是从 n 开始后面的元素就已经是有序的了,下次内循环就到这里就可以了,不用再比较后面的元素。

所以我们需要修改一下内循环的部分:

    public static int[] bubbleSort(int[] origin) {
        if (origin == null || origin.length == 0) {
            return new int[]{};
        }
        System.out.println("origin--->" + Arrays.toString(origin));
        // 记录最后一次交换的位置
        int lastExchangeIndex = 0;
        // 无序数列的边界,每次比较只需要比到这里为止
        int sortBorder = origin.length - 1;
        int index = 0;
        for (int i = 0; i < origin.length - 1; i++) {
            boolean isSorted = true;
            for (int j = 0; j < sortBorder; j++) {
                if (origin[j] > origin[j + 1]) {
                    int temp = origin[j];
                    origin[j] = origin[j + 1];
                    origin[j + 1] = temp;
                    isSorted = false;
                    lastExchangeIndex = j;
                }
                index++;
            }
            System.out.println("第" + (i + 1) + "次比较后" + "origin--->" + Arrays.toString(origin) + ",isSorted--->"
                    + isSorted + ",sortBorder--->" + sortBorder + ",lastExchangeIndex--->" + lastExchangeIndex + ",index--->" + index);
            sortBorder = lastExchangeIndex;
            if (isSorted) {
                break;
            }
        }
        System.out.println("index--->" + index);
        System.out.println("sortedArray--->" + Arrays.toString(origin));
        return origin;
    }

运行结果如下:

可以看到同一个数组,在冒泡排序经过优化后,比较次数减少至三分之一,当然这只是个例不能说明全部,但是这说明优化是很有必要的。

6 总结

时间复杂度

假设原始数组长度为 n,则外循环 n 次,每次遍历嵌套一个内循环,所以时间复杂度为 O(n²)。

空间复杂度

冒泡排序只用到循环,临时变量所占空间不随原始数组的长度改变而改变,所以空间复杂度为 O(1)。

优点

1.冒泡排序适用于小数和整数,实现简单,空间复杂度低,并且属于稳定排序。

缺点

1.冒泡排序效率并不高,每次只能移动相邻两个数据,时间复杂度最高达到 O(n²)。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值