排序之冒泡排序

简介

在计算机科学中,排序占据着十分重要的位置,排序算法属于基础科学。常用的排序算法中包括快速排序、冒泡排序、计数排序、堆排序。我们可以通过分类来记忆这些排序算法

根据时间复杂度可以分为

  • O(n2)
    • 冒泡排序
    • 选择排序
    • 插入排序
    • 希尔排序
  • O(nlog)
    • 快速排序
    • 归并排序
    • 堆排序
  • O(n)
    • 计数排序
    • 桶排序
    • 基数排序

冒泡排序的核心就是相邻元素的比较,通过n-1轮比较达到排序的结果。

冒泡算法(bubble sort),它是一种基础的交换排序。冒泡排序之所以叫做冒泡排序,是因为这种排序算法的每一个元素都可以像小气泡一样,根据自身大小,一点一点的向着数组的一侧移动。

冒泡算法的思想如下:

我们将相邻的元素两两比较,当一个元素大于右侧相邻元素,交换它们的位置;当一个元素小于或者等于右侧相邻元素时,位置不变。通过第一轮冒泡之后,则序列可以分为两个部分,一个是最右侧的有序区域,一个是有序去相对应的无序区。

 冒泡排序是稳定排序,总共遍历n-1轮,平均时间复杂度为O(n2)。

代码演示

初级版本

代码非常简单, 使用双循环进行排序。 外部循环控制所有的回合, 内部
循环实现每一轮的冒泡处理, 先进行元素比较, 再进行元素交换。

package com.atguigu.sort;

import java.util.Arrays;

/**
 * @author songquanheng
 * 2021/2/7-15:49
 */
public class BSort {
    public static void main(String[] args) {
        int[] arr = {5, 8, 6, 3, 9, 2, 1, 7};
        bubbleSort(arr);
        System.out.println("after bubble sort");
        System.out.println(Arrays.toString(arr));
    }

    public static void bubbleSort(int array[]) {
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j]>array[j+1]) {
                    int tmp = array[j];
                    array[j ] = array[j+1];
                    array[j + 1] = tmp;

                    
                }
                count++;
            }
        }

        System.out.println("count = " + count);
    }
}

程序运行之后结果如下:

count = 28
after bubble sort
[1, 2, 3, 5, 6, 7, 8, 9]

其中

28 = 7+6+5+4+3+2+1

i控制趟数,i的值从0取到array.length-1趟。

j则控制内层循环中左边的元素的指向。由于要访问array[j+1],因此j的范围要小于array.length-i-1

数列已经有序的优化

因此,我们可以通过添加标志位来判断某一趟中是否发生了交换,如果没有发生交换,则说明序列已经整体有序了,就不用再进行之后的排序了。

public static void bubbleSort2(int array[]) {
        boolean isSorted;
        for (int i = 0; i < array.length; i++) {
            System.out.println("handle " + i + " round");
            isSorted = false;
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                    isSorted = true;
                }

            }
            if (!isSorted) {
                break;
            }
        }

    }

 利用布尔变量isSorted作为标记,如果在本轮排序中,元素有交换,则说明数列无序;如果没有数列交换,则说明数列已经有序,直接跳出大循环即可。

数组分为有序区和无序区

因为右面的5、6、7、8已经是有序了,因此前4轮的比较都是无意义的。想要优化这种场景,需要确定数列的有序区。

我们可以在每一轮排序后,记录下来最后一次元素交换的位置,该位置即为无序数列的边界,再往后就是有序区了。

public static void bubbleSort3(int array[]) {
    boolean isSorted;
    // 记录最后一次交换的位置
    int lastExchangeIndex = 0;
    // 无序数组的边界,每次比较只要比较到这里即可。
    int sortBorder = array.length - 1;

    for (int i = 0; i < array.length; i++) {
        System.out.println("handle " + i + " round");
        isSorted = false;
        for (int j = 0; j < sortBorder; j++) {
            if (array[j] > array[j + 1]) {
                int tmp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = tmp;
                isSorted = true;
                lastExchangeIndex = j;
            }

        }
        sortBorder = lastExchangeIndex;
        if (!isSorted) {
            break;
        }
    }

}

我们在main函数测试上述的代码:

        bubbleSort3(new int[]{3, 4, 2, 1, 5, 6, 7, 8});
        System.out.println(Arrays.toString(arr));

输出结果如下:

handle 0 round
handle 1 round
handle 2 round
handle 3 round
[1, 2, 3, 5, 6, 7, 8, 9]

上述的结果,确实仅仅比较了4轮,还是挺厉害的。降低了比较的趟数,确实显著的增加了冒泡算法的表现。

这个过程让我想到了张磊在《价值》中p322页提到的精益管理。精益管理与浪费相对,如果正确识别了“浪费”,可能也就理解了精益管理99%的含义。在精益管理的范畴中,浪费是指一切消耗了资源而不创造价值的人类活动,包括需要纠正的错误,生产了无需求的产品以及由此造成的库存和积压,不必要的工序,员工的盲目走动,货物从一地到另一地的盲目搬运,由于上一道工序不及时导致下一道工序的等待,以及商品和服务不能满足消费者要求等等。

优化4-鸡尾酒排序

应用场景为:大部分元素已经有序的情况。

算法真的是和应用的场景密切相关的。

这就是鸡尾酒排序的思路。 排序过程就像钟摆一样, 第1轮从左到右,
第2轮从右到左, 第3轮再从左到右

代码不再附录了,感兴趣的人可直接查阅《漫画算法》P116页。

下载

排序算法之冒泡排序

总结

 我还记得当时我看到冒泡算法的三种优化,以及鸡尾酒排序的欣喜,学无止境是真实不虚的。我们在学习或者工作的时候,思考问题,一定要静的下来,认真的思考其中的问题和原因,深入研究,研究透澈,这样才行的。一个简单的冒泡排序其中就有这么多的弯弯绕绕,如果不是真懂真理解,模模糊糊的知道,见过,对于个人未来的发展是不利的。所以曾国藩的那句“躬身入局”还是值得我辈推崇和提倡的。

 希望大家都做靠谱的人才:

  • 自驱型的人。自驱型的人寻找事情背后的意义,拥有专注解决问题的最佳效率,而不需要更大的组织规模。
  • 时间敏感性的人。浪费时间就是kill people,把有限的时间使用好,是一门非常重要的功课。
  • 有同理心的人,有同理心的人往往习惯于换位思考和通盘考虑,而不是机械的完成任务。
  • 终身学习者。与具有固定性思维的人相比,拥有成长性思维的人更加重视学习和挑战,把学习作为终身的乐趣和成就,而不是短暂的功利性的斩获。
2021年2月7日16:50:11于AUX
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值