冒泡排序之优化

冒泡排序之优化

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

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

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

	**常用的场景是数组排序:即遍历数组,对数组相邻元素进行对比,若想得到升序序列,则将大的元素放后边,小的放前边。若想得到降序数组则反过来**

图文讲解:

冒泡排序:从第一个数据开始,一次比较相邻元素的大小。如果前者大于后者,则进行交换操作。把大的元素往后交换。通过多轮迭代,直到没有交换操作为止。冒泡排序就像是在一个水池中处理数据一样,每次会把最大的那个数据传递到最后。

算法实现:

 @Test
    public void sortTest(){
        int[] arr = {4,1,3,2,6,7,8,9,10,11,13,15};

        int temp = -1;
        int count = 0;
		
        //需要进行arr.length-1次排序遍历
        for (int i=0; i<arr.length; i ++){
			//每次遍历,需要把所有无序的元素遍历完
            for (int j=0 ; j<arr.length-1-i; j++){
				//如果后边的元素比前边小则交换,反之则不变
                if (arr[j+1] < arr[j]){
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
                //记录循环执行的次数
                count ++;
            }
        }
        System.out.println("执行了" + count + "次");
        System.out.println("----------------------------");
        for (int i : arr) {
            System.out.print(i + "\t");
        }
    }

结果

执行了66次
----------------------------
1	2	3	4	6	7	8	9	10	11	13	15	

优化一:

通过上面的图片,我们可以发现。在第三次排序的时候,数组其实已经是一个有序数组了。但循环依然在继续,这就白白的浪费了资源。如果我们能判断出数组是有序的,然后跳出循环,那就节省了一部分空间。

 @Test
    public void sortTest1(){
        int[] arr = {4,1,3,2,6,7,8,9,10,11,13,15};

        int temp = -1;
        int count = 0;
        
        for (int i=0; i<arr.length; i ++){
			//设置一个布尔值,默认为true
            boolean flag = true;

            for (int j=0 ; j<arr.length-1-i; j++){

                if (arr[j+1] < arr[j]){
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                    //如果进行了交换,则把布尔值改为false
                    flag = false;
                }

                count ++;

            } 
            //如果进行一次遍历,发现布尔值仍是true,则说明没有发生元素交换,
            //即数组为有序数组,跳出循环即可
            if (flag ){
                break;
            }
        }

        System.out.println("执行了" + count + "次");
        System.out.println("----------------------------");
        for (int i : arr) {
            System.out.print(i + "\t");
        }
    }

结果:

执行了30次
----------------------------
1	2	3	4	6	7	8	9	10	11	13	15	

在以上优化的基础上,还存在一个问题,例如我们的初始数组如下:

int[] arr = {4,1,3,2,6,7,8,9,10,11,13,15};

我们发现arr在6之后的元素是有序的,根本不需要进行遍历和交换。然而,我们每次遍历是从0开始遍历到arr.length-i-1的。这就有可能包括了部分有序数组。

因此,如果我们能把无序数列和有序数组的边界标出来,每次遍历和交换的时候,只遍历到无序数组则也能省下部分空间。

  @Test
    public void sortTest2(){
        int[] arr = {4,1,3,2,6,7,8,9,10,11,13,15};

        int temp = -1;
        int count = 0;
        //定义最后一次交换位置时的索引
        int lastChangeIndex = 0;
        //初始化,第一次遍历数组时,需要遍历的长度
        int sortBorder = arr.length-1;
        for (int i=0; i<arr.length; i ++){

            boolean flag = true;

            for (int j=0 ; j< sortBorder; j++){

                if (arr[j+1] < arr[j]){
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                    //记录最后一次交换位置时的索引
                    lastChangeIndex = j;
                    flag = false;
                }


                count ++;

            }
            //将最后一次记录的索引,赋值给无序数列的边界,这就是下一次要遍历的数组边界
            sortBorder = lastChangeIndex;
            if (flag ){
                break;
            }
        }

        System.out.println("执行了" + count + "次");
        System.out.println("----------------------------");
        for (int i : arr) {
            System.out.print(i + "\t");
        }
    }
执行了14次
----------------------------
1	2	3	4	6	7	8	9	10	11	13	15	

总结:

​ 冒泡排序的思想其实很简单,就是相邻元素两两对比,判断是否需要交换顺序。但经过细心的考虑,有些特殊的情况,则可以提前确实数组局部有序,则不需再进行重复遍历。优化也就是把这些重复遍历的步骤给去除,最终得到高性能的冒泡排序算法。

posted @ 2020-07-08 15:45  小福子的小小幸福  阅读(192)  评论(0编辑  收藏  举报