是个人都看得懂的冒泡排序

冒泡排序

算法思想

冒泡排序属于一种典型的交换排序。

交换排序顾名思义就是通过元素的两两比较,判断是否符合要求,如过不符合就交换位置来达到排序的目的。冒泡排序名字的由来就是因为在交换过程中,类似水冒泡,小(大)的元素经过不断的交换由水底慢慢的浮到水的顶端。

冒泡排序的思想就是利用的比较交换,利用循环将第 i 小或者大的元素归位,归位操作利用的是对 n 个元素中相邻的两个进行比较,如果顺序正确就不交换,如果顺序错误就进行位置的交换。通过重复的循环访问数组,直到没有可以交换的元素,那么整个排序就已经完成了。

算法描述
  1. 比较相邻的元素,如果前一个比后一个大,交换之。
  2. 第一趟排序第1个和第2个一对,比较与交换,随后第2个和第3个一对比较交换,这样直到倒数第2个和最后1个,将最大的数移动到最后一位。
  3. 第二趟将第二大的数移动至倒数第二位
算法分析

​ 首先我们定义一个非顺序排列的数组

image-20200818215423054

根据冒泡排序的思想,我们比较相邻的两位数,如果前面的值比后面的大,则交换。


上图所示的过程,我们发现冒泡排序的几个特性

  1. 由于只有前面的数字比后面的数字大才会进行交换操作,如果一个数字比其后一位的数字小,一次冒泡排序之后,该数字仍然在比它大的数字的前面。

  2. 第一轮交换之后,并不能保证数组是有序的

  3. 最大的数字会移动到最后,因为最大的数始终大于其他数字,所以一直会进行交换操作,交换操作是当前位置的数字与后一位进行交换,交换完毕,索引值加一,然后下一次判断时,之前被我们换到后面的数字又要进行判断,这个过程一直重复直至我们的索引值移动到数组的尾部。

我么继续下一轮的冒泡

冒泡排序

我们发现是实际上7无需和8进行比较,而且数组中第二大的元素7移动到了倒数第二位,这时候我们可以进行优化,每次冒泡排序的下标所能到达的最大值是上一次的减1,我们开始后面几次的遍历

我们又发现了一个问题:

  1. 当倒数第二次,数组已经有序了,还是会进行条件判断,因为计算机并不理解什么是有序无序,只是根据我们的if(条件)是否成立去执行程序而已,所以这里我们还需要优化。

至此,我们的冒泡排序的分析思路结束了,接下来我们用代码来实现

代码实现


交换数字

由于第一个和第二个数比,第一项要小,所以结果不变

public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};
        
        //如果0大于1,交换
        if (array[0]>array[1]){
            //定义中间变量,用于交换
            int temp=array[0];
            array[0]=array[1];
            array[1]=temp;
        }
    System.out.println(Arrays.toString(array));
    }
[1, 5, 8, 7, 2, 6]

下标移动

我们判断第三和第四个数,对应的下标是 2 和 3

    public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};

        //如果2大于3,交换
        if (array[2]>array[3]){
            //定义中间变量,用于交换
            int temp=array[2];
            array[2]=array[3];
            array[3]=temp;
        }
        System.out.println(Arrays.toString(array));
    }
[1, 5, 7, 8, 2, 6]

下标最大值

按照这个逻辑我们一直将比较的数的下标增加,那么时候结束呢,我们看一下下面的程序

    public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};

        //如果0大于1交换
        if (array[6]>array[7]){
            //定义中间变量,用于交换
            int temp=array[6];
            array[6]=array[7];
            array[7]=temp;
        }
        System.out.println(Arrays.toString(array));
    }
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
	at net.dulao.Demo1.main(Demo1.java:20)

我们的初始下标是0,每次都和后一位进行比较,所以下标达到数组最大长度-1时,是最后一次了。

至此我们的基本逻辑分析完毕,我们走一遍代码。

public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};
        for (int i = 0; i < array.length-1; i++) {
            //如果0大于1交换
            if (array[i]>array[i+1]){
                //定义中间变量,用于交换
                int temp=array[i];
                array[i]=array[i+1];
                array[i+1]=temp;
            }
            System.out.println(Arrays.toString(array));
        }
    }
[1, 5, 8, 7, 2, 6]
[1, 5, 8, 7, 2, 6]
[1, 5, 7, 8, 2, 6]
[1, 5, 7, 2, 8, 6]
[1, 5, 7, 2, 6, 8]

每一趟冒泡排序的最大下标

这是第一趟排序,我们看到最大的8像泡泡一样的往上浮动,直至达到数组的尾部,我们开始第二次冒泡排序,这次我们i能到达的最大值是上一次的-1,也就是array.length-2;

    public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};
        for (int i = 0; i < array.length-1; i++) {
            //如果0大于1交换
            if (array[i]>array[i+1]){
                //定义中间变量,用于交换
                int temp=array[i];
                array[i]=array[i+1];
                array[i+1]=temp;
            }
            //System.out.println(Arrays.toString(array));
        }

        for (int i = 0; i < array.length-2; i++) {
            //如果0大于1交换
            if (array[i]>array[i+1]){
                //定义中间变量,用于交换
                int temp=array[i];
                array[i]=array[i+1];
                array[i+1]=temp;
            }
            System.out.println(Arrays.toString(array));
        }
[1, 5, 7, 2, 6, 8]
[1, 5, 7, 2, 6, 8]
[1, 5, 2, 7, 6, 8]
[1, 5, 2, 6, 7, 8]

我们发现第二次循环的开始值也是0,结束时上一次结束的最大索引-1,于是我们可以把循环嵌套起来

public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};
        for (int i = 0; i < array.length-1; i++) {
            for (int j = 0; j < array.length-1-i; j++) {
                //如果0大于1交换
                if (array[j]>array[j+1]){
                    //定义中间变量,用于交换
                    int temp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                }
            }
            //我们的逻辑已经清晰,所以打印语句移动内侧循环结束之后再打印
            System.out.println(Arrays.toString(array));
        }
    }

排序优化

我们外侧循环i可以代表循环的次数,也就是冒泡的次数,一共要冒泡排序array.length-1从,

循环里面记录的是具体每一趟冒泡排序的开始值,结束值,我们发现第一次的i为0,我们可以让循环的终止条件改为array.length-i-1,这样第一次冒泡排序也能让最大下标为array.length-1,第二次array.length-2…

我们的冒泡排序的代码就算完成了,我们跑一遍。

[1, 5, 7, 2, 6, 8]
[1, 5, 2, 6, 7, 8]
[1, 2, 5, 6, 7, 8]
[1, 2, 5, 6, 7, 8]

嗯,完成是完成了,但是我们看最后两次是重复的,我们需要给冒泡排序加一个结束条件:如果一次完整的冒泡排序之后,没有数字进行交换,说明已经排好了,我们跳出循环。

我们立一个flag,如果进入if的语句块中,说明在进行交换,我们立flag失败,置为false。如果没走if的语句那么flag还是true,我们进行一些操作,这是我们的优化思路

boolean flag=true;
if (array[j]>array[j+1]){
    //定义中间变量,用于交换
    int temp=array[j];
    array[j]=array[j+1];
    array[j+1]=temp;
    flag=false;
}
if(flag){
    ......
}

我们的flag应该立在两个for之间,也就是说一趟冒泡排序之后,我们的flag都为true,说明没有一次if是成立的,数组已经有序,我们break跳出外圈循环

最终代码

public static void main(String[] args) {
        //接收一个数组
        int[] array={1,5,8,7,2,6};
        for (int i = 0; i < array.length-1; i++) {
            boolean flag=true;
            for (int j = 0; j < array.length-1-i; j++) {
                //如果0大于1交换
                if (array[j]>array[j+1]){
                    //定义中间变量,用于交换
                    int temp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                    flag=false;
                }
            }
            if (flag){
                break;
            }
            System.out.println(Arrays.toString(array));
        }
    }
[1, 5, 7, 2, 6, 8]
[1, 5, 2, 6, 7, 8]
[1, 2, 5, 6, 7, 8]

这样我们的代码逻辑就算完成了。

总结

总结个锤子,黑科技还没上呢!

冒泡排序源码

public class BubbleSort implements SortAlgorithm {

    /**
     * 排序
     *
     * @param array 数组
     */
    @Override
    public void sort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            boolean flag=true;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j]>array[j+1]){
                    //交换
                    reverse(array,j,j+1);
                    flag=false;
                }
            }
            //如果一次冒泡排序没发生数据交换,说明一件排序好了;
            if(flag){
                return ;
            }
        }
    }

}

测试

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    //可行性测试
	simpleTest(BubbleSort.class);	
    //压力测试
	timeComplexityTest(BubbleSort.class);
}

===================BubbleSort=====================
原数组[-361, -452, 485, 326, 239, -104, -19, -102, -426, 221]
排序后数组[-452, -426, -361, -104, -102, -19, 221, 239, 326, 485]
排序结果正确!
开始计算....
BubbleSort:11204ms

怎么用,有点意思吧,关于这个测试方法的讲解我放在了这里

知识总结:
  • 冒泡排序法:也叫升序排序法,但是相比起二分法查找只能应用于有序数列,二如何将一个无序数列变的有序就可以使用冒泡排序法!!!

  • **空间时间复杂度:**通过分析冒泡排序的实现代码可以得知,该算法的最差时间复杂度为O(n2),最优时间复杂度为O(n),平均时间复杂度为 O(n2)

  • 优化:一趟冒泡排序没交换操作则表示排列完成

彩蛋:
//假如我们不加这一行
if(flag){
    // return ;
}
原数组[289, 425, 3, 131, -197, 140, 197, -382, -165, 260]
排序后数组[-382, -197, -165, 3, 131, 140, 197, 260, 289, 425]
排序结果正确!
开始计算....
BubbleSort:10634ms

额,这个优化就意思一下,可能快个5%吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值