冒泡排序(思路,代码,优化详解)

冒泡排序(思路,代码,优化详解)

思路

思路:每一轮循环,通过俩俩比较,都会将一个最值"冒泡"出来。

比如下面的数组{2,5,3,8,6,7},若从小到大排序

第一轮循环会把最小的元素冒泡出来,就是2

第二轮循环会把第二小的元素冒泡出来,就是3

最多5次循环可以完成排序,也就是length-1次

在这里插入图片描述

private static int[] BubbleSort(int[] nums) {
    //冒泡排序
    //nums.length-1轮循环,每一轮循环,俩俩比较,找到一个最值,冒泡。
    for(int i=1;i< nums.length;i++){
        for(int j= nums.length-1;j>=i;j--){
            //俩俩比较,小的放前面
            if(nums[j]<nums[j-1]){
                //交换
                swap(nums,j,j-1);
            }
        }
    }
    return nums;
}
private static void swap(int[] nums, int i, int j){
    int temp=nums[i];
    nums[i]=nums[j];
    nums[j]=temp;
} 
public static void main(String[] args) {
    int[] nums={2,5,3,8,6,7};
    System.out.println(Arrays.toString(BubbleSort(nums)));//[2, 3, 5, 6, 7, 8]
}

优化1:设置有序标志位flag

对于{2,5,3,8,6,7}

正常来说要进行5轮循环,每一轮冒泡出一个最值

但数组{2,5,3,8,6,7},第2轮循环后变为{2,3,5,6,7,8}。已经有序,所以剩下的循环是没有意义的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以怎么避免“傻乎乎”地再去循环呢?那我们需要确定当前数组是有序的,有序了我们才可以不去循环了,对吧

那么如何确定数组已经有序了呢?

所以优化1:若上一次循环中,没有进行交换,表明数组已经有序就提前结束

试想一下:如果上一轮发生了交换,那我们不确定数组是否有序,还要循环一次看一下。

如果没有发生交换,那肯定就是有序了呀

如果用if-else架构,那么它应该是这样的逻辑:

if(!swap)//上一轮没交换过
  then 不用看,数组有序了
else
  不确定,再来一轮试试看

实现:记录标志位isSwap,若发生交换则重置为true,在每一轮循环前判断isSwap==true,是则说明上一轮进行过交换,这一轮循环是有意义的;否则直接break结束。

private static int[] BubbleSort1(int[] nums) {
        //冒泡排序
        //nums.length-1轮循环,每一轮循环,俩俩比较,找到一个最值,冒泡。
        //优化1:记录标志位isSwap,若上一轮循环没有交换过,
    	//		则说明数组已经有序,直接break
        boolean isSwap=true;
        for(int i=1;i< nums.length;i++){
            //先检查标志位,判断数组是否已经有序
            if(!isSwap){
                break;
            }
            //每一轮初始化为false
            isSwap=false;
            for(int j= nums.length-1;j>=i;j--){
                //俩俩比较,小的放前面
                if(nums[j]<nums[j-1]){
                    //交换
                    swap(nums,j,j-1);
                    isSwap=true;//标志位为true,说明交换过,继续下一轮
                }
            }
        }
        return nums;
    }

优化2:设置结束边界lastSwapIndex

对于优化1,我们只是用“上一轮循环是否交换”来笼统地判断了一下“数组是否有序”。

如果有序了,就不排了;反之则循环i。

可范围实在过大,所以我们需要缩小判断”有序“的范围

为了更好的说明问题,我们需要换个例子

假设现在有数组{1,2,3,4,6,7,5}。我们发现这个数组好像是基本有序的(至少前几个很有序)

如果正常情况下(演示)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们看到这个序列,第一轮比较6次,第二轮是5次

但是第二轮比较时还是有点“傻”,至少5次比较很没必要吧

如果我们对优化1节省的循环次数还不满意,可以继续优化

我们发现:上一轮swap的位置之前,数组肯定是有序的(因为如果不有序,它还会继续swap,更新swap的位置,而一旦确定,在swap位置之前的数组序列肯定是有序的)

那么也就说明:也许以前j一直从length-1遍历到i是可以改变的。

如果用lastSwapIndex代替最后swap的位置的话,j只需要遍历到lastSwapIndex即可

我们再回过头看之前的例子,但这次我们会记录lastSwapIndex,看一下它节省了多少。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这次j从末尾往前遍历,只遍历到lastSwapIndex,比较了2次

不过这个优化并没有改变”冒泡不行“的事实

优化2:设置结束边界lastSwapIndex,记录上一次最后交换的位置,作为下一次循环的结束边界

这个优化的逻辑是:上一次最后交换的位置之前,数组元素是有序的,因为没发生交换

所以呢,j在遍历的时候只需要从nums.length-1遍历到lastSwapIndex即可。

也可以这样理解,原来j遍历到i,是因为每一轮冒泡出来一个最值,这个最值是有序的,

比如第一轮出来最小的2,第二轮出来次小的3,然后第三轮呢就遍历到j>=i(i=3即可),因为每一轮出来一个最值,它的排列就是有序的。

而我们通过记录上次最后交换的位置,知道了数组已经有序的序列:就是nums[0]到nums[lastSwapIndex]。所以nums.length-1遍历到lastSwapIndex即可。

public static SortResult BubbleSort_b1(int[] nums){
    //优化1:设置标志位,如果上一轮循环没有交换过元素,说明后面已经有序,直接进入下一轮
    //优化2:设置结束边界,记录上一次最后交换的位置,作为下一次循环的结束边界,比如
    SortResult sortResult = new SortResult(nums);
    boolean isSwap=true;
    int lastSwapIndex=1;
    for(int i=1;i< nums.length;i++){//如果isSwap为false,则直接下一轮
        if(!isSwap){
            break;
        }
        isSwap=false;
        int end=lastSwapIndex;
        for(int j= nums.length-1;j>=end;j--){//遍历到lastSwapIndex即可,不需要遍历到i
            sortResult.incrementComparisons();
            if(nums[j]<nums[j-1]){
                swap(nums,j,j-1);
                isSwap=true;//标志位为true,说明交换过,继续下一轮
                lastSwapIndex=j;//记录交换的下标,以便下一轮j的遍历
                sortResult.incrementSwaps();
            }
        }
        System.out.println("第"+i+"次循环:"+ Arrays.toString(nums));
    }
    return sortResult;
}

总结

冒泡排序的思路是:每一轮循环,俩俩比较,冒泡出一个最值,经过最多length-1次循环,把数组排序

优化思路是:

  • 判断数组何时已经排好序,i不再多余循环 = > 记录标志位isSwap
  • 判断数组已经排好序的范围,j不要多余遍历 => 设置结束边界lastSwapIndex

blog referenece

https://mrfzh.github.io/2019/11/17/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%8F%8A%E5%85%B6%E4%BC%98%E5%8C%96%EF%BC%88%E4%B8%89%E7%A7%8D%E4%BC%98%E5%8C%96%EF%BC%89/

https://www.jerrymei.cn/how-to-make-algorithm-visualizer/

https://chat.openai.com/

https://blog.csdn.net/dongming8886/article/details/123458790?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171464035216800178581362%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=171464035216800178581362&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-2-123458790-null-null.142

http://www.donghuasuanfa.com/sort

https://visualgo.net/zh/sorting?slide=6-6

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程就是n踢r

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值