美文网首页
常见算法1-冒泡及优化

常见算法1-冒泡及优化

作者: 封号斗罗 | 来源:发表于2020-06-28 03:00 被阅读0次

    冒泡算法:

    (对一些部分有序的数组,效率高)
    时间复杂度:n^2;
    一般入门时是这样写的:

            int [] datas= new int[]{6,5,4,3,2,1};
            for (int i=0;i<datas.length;i++){
                for (int j=0;j<datas.length-1;j++){
                    if (datas[j]>datas[j+1]){
                        int minV=datas[j];
                        datas[j]=datas[j+1];
                        datas[j+1]=minV;
                    }
                }
            }
           for (int data : datas) {
                  System.out.print(data+" ");
           }
    

    得道结果是12346;没问题;
    但是加上打印每一步骤的结果值,发现还有些执行相同的
    多次,导致浪费计算次数。于是想到了优化;


    image.png
    冒泡算法的优化:
    优化1

    1.首先是第二个for循环的总次数:
    由打印的日志分析知道,每执行一次内层的循环一次,就可以将大的值给冒泡交换到最后面了。最大数,第一遍就给放到最后面了,因此可以不用再交换后面排好序的位置了。
    由打印结果规律,归纳推导,发现外层遍历的下标刚好是内层循环排好序的个数。于是内层的遍历可以在后面便利的总数减去外层遍历下标。
    2.内层循环遍历时,有一个最小比对值,需要频繁的申明,使用,再释放。因此可以放到最外面,两个循环的初始下标计量也可以放入外边统一声明。
    于是得到如下:

            int [] datas= new int[]{6,5,4,3,2,1};
            int change,m=0,n;
            for (;m<datas.length;m++){
                n=0;
                for (;n<datas.length-1;n++){
                    if (datas[n]>datas[n+1]){
                        change=datas[n];
                        datas[n]=datas[n+1];
                        datas[n+1]=change;
                    }
                    System.out.format("第 %d 遍的第%d 次交换:", m+1,n+1);
                    for(int count:datas) {
                        System.out.print(count+" ");
                    }
                    System.out.println();
                }
                System.out.format("第 %d 遍最终结果:", m+1);
                for (int data : datas) {
                    System.out.print(data+" ");
                }
                System.out.println();
            }
    

    结果是:


    image.png

    发现最后的结果明显内循环每次都减少了次数。

    优化2

    但是如果数据是321456呐?
    结果还是会走那么多次数,而且再有些步骤中就已经排好序了,可是外层循环还要继续排序。


    image.png

    于是,为了这个想去优化问题,我们可以设置一个标志位,用来表示当前第m趟是否有交换,如果有则要进行m+1 趟,如果没有,则说明当前数组已经完成排序。实现代码如下

            int [] datas= new int[]{3,2,1,4,5,6};
            int change,m=0,n;
            boolean flag;
            for (;m<datas.length;m++){
                n=0;
                flag=true;
                for (;n<datas.length-1-m;n++){
                    if (datas[n]>datas[n+1]){
                        change=datas[n];
                        datas[n]=datas[n+1];
                        datas[n+1]=change;
                        //如果还有没排好序的,将标志位设置为false;
                        flag=false;
                    }
                    System.out.format("第 %d 遍的第%d 次交换:", m+1,n+1);
                    for(int count:datas) {
                        System.out.print(count+" ");
                    }
                    System.out.println();
                }
                System.out.format("第 %d 遍最终结果:", m+1);
                for (int data : datas) {
                    System.out.print(data+" ");
                }
                System.out.println();
                //如果没有要交换位置的,就证明排好序了,直接退出整个循环
                if (flag){
                    return;
                }
            }
    

    打印结果:


    image.png

    发现好多了,少外层的两次循环。

    优化3

    但是还有一个问题,就是上图中,明明第二次遍历,第一次的交换就已经完成了,但是后面还要继续交换,造成了一定的时间浪费。
    对于这种问题,我们可以想到一个解决方案,利用一个标志位,记录一下当前第m趟所交换的最后一个位置的下标,在进行第m+1 趟的时候,只需要内循环到这个下标的位置就可以了,因为后面位置上的元素在上一趟中没有换位,这一次也不可能会换位置了。基于这个规律,我们可以进一步优化我们的代码:

            int [] datas= new int[]{3,2,1,4,5,6};
            int change,m=0,n,changelen=datas.length-1,tempIndex=0;
            boolean flag;
            for (;m<datas.length;m++){
                n=0;
                flag=true;
                for (;n<changelen;n++){
                    if (datas[n]>datas[n+1]){
                        change=datas[n];
                        datas[n]=datas[n+1];
                        datas[n+1]=change;
                        //如果还有没排好序的,将标志位设置为false;
                        flag=false;
                        tempIndex=n;
                    }
                    System.out.format("第 %d 遍的第%d 次交换:", m+1,n+1);
                    for(int count:datas) {
                        System.out.print(count+" ");
                    }
                    System.out.println();
                }
                changelen=tempIndex;
                System.out.format("第 %d 遍最终结果:", m+1);
                for (int data : datas) {
                    System.out.print(data+" ");
                }
                System.out.println();
                //如果没有要交换位置的,就证明排好序了,直接退出整个循环
                if (flag){
                    return;
                }
            }
    
    

    打印结果:


    优化三

    从打印输出来看,明显减少了很多不必要的计算。比原来的更加优良,减少了时间复杂度。

    选择排序:

    (适用于无序不重复的数列)
    时间复杂度:n^2;
    原理:
    第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
    代码:

        public void selectSort(){
            int change,m=0,n;
            int [] datas= new int[]{8,1,48,92,12,77,66,6};
            for (;m<datas.length;m++){
                change=datas[m];
                for (n=m+1;n<datas.length;n++){
                    if (datas[n]<change){
                        change=datas[n];
                        datas[n]=datas[m];
                        datas[m]=change;
                    }
                }
            }
            for (int data : datas) {
                System.out.print(data+" ");
            }
        }
    

    打印结果:
    1 6 8 12 48 66 77 92 ;

    相关文章

      网友评论

          本文标题:常见算法1-冒泡及优化

          本文链接:https://www.haomeiwen.com/subject/lrimnctx.html