冒泡排序及优化代码

  • 冒泡排序和 两个优化。在一些例子中,也能通过冒泡排序,很少的比较次数就能排好顺序

  • 简述冒泡排序思想:
    每一轮循环,通过交换的形式,从一组乱序的数组中选出一个最大/最小数来,将该数放到乱序的数组中最后或最前的位置上。

  • 下面的例子都是以从小到大稳定排序

  • 稳定排序

  • 平均时间复杂度:0(n2),最好情况是:0(n)

一、冒泡排序之青铜版本:

1、排序思想:

从第一个数,依次往后进行比较,若第一个大于第二个数(第二个数大于第三个数…),则交换。 从而每一轮for循环,比较出一个最大或最小的数,放在最后确定的位置。

假设有n个数:
就需要循环n-1次。外循环,用i表示,循环次数
每轮循环需要比较n-1-i次 内循环,用j表示,每次循环比较次数

  • 为什么要循环n-1轮? (n是数组个数)
    例子:{2,3,1}从小到大排序。
    第一轮循环比较:2<3 不交换;3<1 不成立,则交换{2,1,3}。 第一轮结果数组是:{2,1,3}
    第二轮循环比较:2<1 不成立,则交换{1,2,3};2<3不交换。 第二轮结果数组是:{1,2,3}
    所以说:最多经过n-1轮的循环比较,就可以将乱序,排列好。

  • 为什么每轮循环比较n-1-i次?
    第一轮比较次数:n-1-i :那就是3-1-0=2次 【有三个数,比较两次就可以得出最后结果】
    第二轮比较次数:n-1-i :那就是3-1-1=1次 【有两个数,比较一次就可以得出最后结果】
    例子:{2,3,1}从小到大排序
    第一轮比较:2<3 不交换;3<1 不成立,则交换{2,1,3}。 第一轮结果数组是:{2,1,3}
    经过第一轮的循环比较,就会得出3是最大值,并放在了乱序数组的最后位置,那么下一轮就不需要再次比较,前一个数和3的大小了。
    第二轮比较:2<1 不成立,则交换{1,2,3}; 第二轮结果数组是:{1,2,3}

代码:

public static void bubble1(){
        int count=0;      			   //统计一共比较多少次数
        int[] arr = {2,1,3,4,5};       //要进行排序的数组——乱序的数组
        int n = arr.length;  		   //数组中有几个数
		System.out.print("排序前:");
        print_Array(arr, n);          //打印未排序的数组
        
        //冒泡排序算法:
        for (int i = 0; i < n-1; i++) {        //一共循环n-1次
            for (int j = 0; j < n-1-i; j++) {  //每轮循环比较n-1-i次
                count++;                       //统计比较次数
                if(arr[j]>arr[j+1]) {          //第一个数和第二个数比较
                    swap(arr,j,j+1);           //若第j个数大于第j+1个数,则交换
                }
            }
            p_Array(arr,n,i);                    //打印每轮循环后数组的样子
        }
        System.out.print("排序后:");
        print_Array(arr, n);          //打印数组
        System.out.println("总共比较次数:"+count);
   }

执行结果:
在这里插入图片描述

2、发现问题:

从上面每轮比较结果,就可以看出2,3,4轮循环是没有必要进行比较的,因为在第一轮比较时,就已经排好序了。
那么我们有没有办法,减少循环的次数?

二、冒泡排序之黄金版本:

1、排序思想:

那我们可不可以设置一个变量,如果经过一轮外循环,没有进行交换,那么就说明,数组已经排好序了。
代码:

 public static void bubble2(){
        int test = 0;
        /*test用来表示这一轮外循环,是否有交换
        如果一轮的循环,没有交换,那就是已经排好序了。那么就退出。
        1假如可能排好序了,0代表肯定没有排好序*/
        int count=0;                        //总共比较次数
        int[] arr = {2,1,3,4,5};            //排序的数组
        int n = arr.length;                 //数组中有几个数
        System.out.print("排序前:");
        print_Array(arr, n);                //打印乱序的数组

        //冒泡排序算法:
        for (int i = 0; i < n-1; i++) {        //一共循环n-1次
            test=1;                            //默认排好序了
            for (int j = 0; j < n-1-i; j++) {  //比较n-1-i
                count++;                 	   //统计总共比较次数
                if(arr[j]>arr[j+1]) {          //第一个数和第二个数比较
                    test =0;                   //说明目前没有排好序
                    swap(arr,j,j+1);           //若第j个数大于第j+1个数,则交换
                }
            }
            p_Array(arr,n,i);                  //打印每轮循环后的数组
            if(test==1) break;
            //经过一轮的比较,如果没有交换,说明已经排好序了,那么就退出循环。完成排序
            
        }

        System.out.print("排序后:");
        print_Array(arr, n);					  //打印排好序的数组
        System.out.println("总共比较次数:"+count);//打印一共比较的次数
    }

在这里插入图片描述

2、发现问题:

黄金方法,虽然已经从4轮减少到2轮循环了,从而解决了大部分重复比较的次数。那么还是有问题的,当执行完第一轮时,就已经排好序了,但是还有执行了第二轮,在第二轮中跳出了循环。
通过这个例子中,那有没有方法,经过一轮的比较,并且只需要比较4次就可以排好顺序呢?

三、冒泡排序之铂金版本:

1、排序思想:

控制内循环的比较次数,每当一轮循环后,记录最后一次交换的索引位置j。(说明这个位置的后面,已经排好序了。那么我们下一轮外循环时,就不需要比较,这个位置及往后的数了。)

public static void bubble3(){
        int count=0;                        //定义总共比较次数
        int[] arr = {2,1,3,4,5};            //要进行排序的数组
        int n = arr.length;                 //数组中有几个数

        int test = 0;
        /*test用来表示这一轮外循环,是否有交换
        如果经过一轮的循环比较,没有发现交换,那就是已经排好序了。那么就退出。
        1假如可能排好序了,0代表肯定没有排好序 */
        int p1=0; 		  //记录最后一次交换位置,下一次就不再比较这个位置及以后的位置。
        int p2=n-1;
        /*由于内循环j变量,每次都需要判断j的最大比较索引的位置,所以说在外循环一轮的比较重,j是不能变的。
        所以需要引出p2,来记录本轮最后一次交换的位置,也就是最后一个未排好序的索引位置。(p2之后的数组都是一件排好序了)
        下次i轮循环时,j只需要比较到P记录的最后一个未排好序的数即可,后面排好序的,不需要再次比较了。
        那么初始化,第一次比较次数应该是n-1次。(假设3个数,则n=3,只需要比较n-1=2次,就可以比较出最大的数)*/

        System.out.print("排序前:");
        print_Array(arr, n);                //打印数组

        //冒泡排序算法:
        for (int i = 0; i < n-1; i++) {        //一共循环n-1次
            test=1;                            //默认排好序了
            for (int j = 0; j < p2; j++) {     //比较n-1-i
                count++;               		   //统计比较次数
                if(arr[j]>arr[j+1]) {          //第一个数和第二个数比较
                    test =0;                   //说明目前没有排好序
                    swap(arr,j,j+1);           //若第j个数大于第j+1个数,则交换
                    p1 = j;
                }
            }
            p2=p1;				//下一轮比较的最大索引位置
            if(test==1) break;  //经过一轮的比较,如果没有交换,说明已经排好序了
            p_Array(arr,n,i);   //每轮循环的数组

        }
        System.out.print("排序后:");
        print_Array(arr, n);
        System.out.println("总共比较次数:"+count);
    }

在这里插入图片描述

2、发现问题:

那么这个例子{2,1,3,4,5},从固定循环n-1次,和固定比较n+(n-1)+(n-2)+…1次。到只需要循环1次,比较n-1次,即可完成排序
优化的结果:5个数,只需要循环1次,比较4次,那么就可以完成最后的排序。

四、所有代码:

欢迎评论,交流更好的优化方法。
若有错误,请大家多多包涵!

package com.sort;
/*
 * n是数组个数,i代表循环次数,j代表每轮比较次数,count是算法一共比较的次数,
 * 冒泡排序:例子是从小大到排序
 *
 * 规律:
 *   从第一个数,依次往后进行比较,若第一个大于第二个数,则交换。(稳定排序)
 *   每一轮for循环,比较出一个最大或最小的数,放在确定的位置。

 *
 *
 * */
public class BulleSort {
    public static void main(String[] args) {
        bubble1();
        /*
        * bubble1();
        * 循环次数:
        *   n-1次    比如3个数,最多只需要循环2次,就可以得出结果
        * 每轮比较次数:
        *   n-1-i次  第一轮比较n-1,第二轮比较n-2,最后一轮比较n-1-(n-2)=1次
          这个算法,是固定循环n-1次,并且固定每轮比较次数:[n*(n-1)]/2
        * 缺点:
           当出现只需要交换一次的数组:{2,1,3,4,5},再用上面方法就资源浪费了
            排序前:
            2,1,3,4,5,
            排序后:
            1,2,3,4,5,
            总共比较次数:10  //优化后其实只需要比较7次就可以了
        */

       bubble2();
        /*
        * 优化:
        *   减少不必要的循环和比较次数。
        * 目标:
        *   数组{2,1,3,4,5},只需要循环2次,比较前两次循环的次数:(n-1)+(n-2)
        * 结果:
            排序前:
            2,1,3,4,5,
            排序后:
            1,2,3,4,5,
            总共比较次数:7

        */
        bubble3();
        /*
        * 经过上面的优化,可以将固定的比较次数,减少到循环2次,比较前两次循环次数
        * 那么有没有更好的优化方法呢?
           比如只要循环一次,就可以知道下次从没有确定最终位置的地方开始
        * 目标:
        *  1:数组{2,1,3,4,5},只需要循环1次,比较第1次循环的次数:(n-1)
        *  2:数组{1,2,3,5,4},只需要循环2次,比较前两次即可,是因为方法buble2()的test=1/0
        * 方法:
        *   记录每次最后一次交换的位置,说明这个位置往后,都是已经排好序了
        *   那么下轮比较时,只需要比较到上次记录索引的位置即可。
        *
        * */

    }

    public static void bubble3(){
        System.out.println("\n"+"方法三:每次循环,只需要比较未确定最终位置的数");
        int count=0;                        //总共比较次数
        int[] arr = {2,1,3,4,5};           //排序的数组
        int n = arr.length;                 //数组中有几个数

        int test = 0;
        /*如果某一次循环,没有交换,那就是已经排好序了。那么就退出。
        1假如可能排好序了,0代表肯定没有排好序 */
        int p1=0;   // 记录最后一次交换位置
        int p2=n-1;
        /*由于j循环,每次都需要判断j的本次i轮循环的最大比较位置
        所以需要引出p2,来通过本次确定已经排好序的位置,
        下次i轮循环时,j需要比较最大索引位置即可
        那么初始化,第一次比较次数应该是n-1次*/

        System.out.print("排序前:");
        print_Array(arr, n);                //打印数组

        //冒泡排序算法:
        for (int i = 0; i < n-1; i++) {        //一共循环n-1次
            test=1;                            //默认排好序了
            for (int j = 0; j < p2; j++) {  //比较n-1-i
                count++;                 //统计比较次数
                if(arr[j]>arr[j+1]) {          //第一个数和第二个数比较
                    test =0;                   //说明目前没有排好序
                    swap(arr,j,j+1);       //若第j个数大于第j+1个数,则交换
                    p1 = j;
                }
            }
            p2=p1;
            //下一轮比较的最大索引位置

            if(test==1) break;
            //经过一轮的比较,如果没有交换,说明已经排好序了
            p_Array(arr,n,i);                    //每轮循环的数组
        }
        System.out.print("排序后:");
        print_Array(arr, n);
        System.out.println("总共比较次数:"+count);
    }

    public static void bubble2(){
        System.out.println("\n"+"方法二:假如一次循环都没有交换,则说明已排好序");
        int test = 0;
        //如果某一次循环,没有交换,那就是已经排好序了。那么就退出。
        //1假如可能排好序了,0代表肯定没有排好序
        int count=0;                        //总共比较次数
        int[] arr = {2,1,3,4,5};           //排序的数组
        int n = arr.length;                 //数组中有几个数
        System.out.print("排序前:");
        print_Array(arr, n);                //打印数组

        //冒泡排序算法:
        for (int i = 0; i < n-1; i++) {        //一共循环n-1次
            test=1;                            //默认排好序了
            for (int j = 0; j < n-1-i; j++) {  //比较n-1-i
                count++;                 //统计比较次数
                if(arr[j]>arr[j+1]) {          //第一个数和第二个数比较
                    test =0;                   //说明目前没有排好序
                    swap(arr,j,j+1);       //若第j个数大于第j+1个数,则交换
                }
            }
            p_Array(arr,n,i);                    //每轮循环的数组
            if(test==1) break;
            //经过一轮的比较,如果没有交换,说明已经排好序了
        }

        System.out.print("排序后:");
        print_Array(arr, n);
        System.out.println("总共比较次数:"+count);
    }

    public static void bubble1(){
        System.out.println("方法一:未优化");
        int count=0;         //统计一共比较多少次数
        int[] arr = {2,1,3,4,5};           //排序的数组
        int n = arr.length;  //数组中有几个数
        System.out.print("排序前:");
        print_Array(arr, n);          //打印数组

        //冒泡排序算法:
        for (int i = 0; i < n-1; i++) {        //一共循环n-1次
            for (int j = 0; j < n-1-i; j++) {  //比较n-1-i
                count++;                       //统计比较次数
                if(arr[j]>arr[j+1]) {          //第一个数和第二个数比较
                    swap(arr,j,j+1);       //若第j个数大于第j+1个数,则交换
                }
            }
            p_Array(arr,n,i);                    //每轮循环的数组
        }

        System.out.print("排序后:");
        print_Array(arr, n);          //打印数组
        System.out.println("总共比较次数:"+count);
    }


    //打印数组
    private static void print_Array(int[] arr, int length) {
        for (int i = 0; i < length; i++) {
            System.out.print(arr[i] + ",");
        }
        System.out.println();
    }
    //交换数组中两个数的位置
    public static void swap(int[] a,int a0,int a1){
        int temp = a[a0];
        a[a0] = a[a1];
        a[a1] = temp;
    }

    //打印每循环一轮的数组
    public static void p_Array(int[] arr,int length,int i1){
        System.out.print("第"+(i1+1)+"轮循环的数组:");
        for (int i = 0; i < length; i++) {
            System.out.print(arr[i] + ",");
        }
        System.out.println();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值