排序算法(一)冒泡排序

本文详细介绍了一种简单直观的排序算法——冒泡排序。通过对比相邻元素并交换位置的方式,逐步将数组按升序排列。文章提供了Java实现代码,并讨论了两种优化方案,以减少不必要的比较次数。

问题

给定n个整数,将它们按照(非降)序排序。


观察

排序算法有很多种,今天就介绍一个简洁且容易理解的算法——冒泡排序

熟悉规则

接下来观察以下两张图片,熟悉一下游戏规则。

有序序列


  • 这张图片告诉我们:有序序列中,任意一对相邻元素有序

来看另一张图片
无序序列

  • 这张图片告诉我们:无序序列中,总有一对相邻元素无序

我们要做的


我们要做的是 依次比较每一对相邻元素,若逆序,交换之。
若整趟都没有进行交换,则排序完成。否则,再做一趟。


Java实现

简单实现

 private void bubblesort1(int a[], int n) {
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < n - i - 1; j ++) {
                if (a[j] > a[j + 1]) {
                    int temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }
        }
    }

助记码

i∈[0,N-1)                //循环N-1遍
   j∈[0,N-1-i)            //每遍循环要处理的无序部分
     swap(j,j+1)          //两两排序(升序/降序)

伪代码

function bubble_sort (array, length) {
    var i, j;
    for(i from 0 to length-1){
        for(j from 0 to length-1-i){
            if (array[j] > array[j+1])
                swap(array[j], array[j+1])
        }
    }
}

优化

优化1

也许你也发现了,上面的两个算法都有两个for循环,也就是说是O(n^{2})时间复杂度,即使对于[1, 2 , 3, 4 , 5 , 6 ]这样的数组输入,也需要比较N^2次,那么能不能针对这一点进行优化呢?优化的结果,使对于上述那样的良性数组输入,可以节省点比较次数。你看,我下面这段代码,有没有达到这个目的?

private void bubbleSort3(int A[], int n) {  //输入数组A,其中有n个元素
        System.out.println("游戏开始咯");
        boolean sorted = false;                 //sorted为整体有序标志,我们首先假定数组 未排序
        while ( !sorted ) {                     //设置了一个while循环,当sorted为true时,不进入循环,即排序完成.
            //那么,何时sorted为true,又该怎么设置呢? 别急,往下看

            sorted = true; //这里怎么为true了?毫无道理,这么突兀?实际上,这里只是假定已经排序,
                            // 所以设置为true只是个假设,因为下面可能要清除这个true标志
            for (int i = 1; i < n; i ++) { //对数组进行一趟(只能保证进行一趟)循环遍历,但是while外循环保证进行多趟,直到排序完成
                if (A[i-1] > A[i]) {
                    //交换
                    int temp = A[i-1];
                    A[i-1] = A[i];
                    A[i] = temp;

                    sorted = false;//若发现了局部无序,就算局部调整过来了,那谁知道整体是不是有序的,所以清除有序标志。
                }
            }
            n = n - 1; //排序规模递减,因为每进行一个for循环,则有一个最大的数排到最后,它已经找到自己的位置,不需要再排序,也不需要比较啦
        }

    }

另外呢,神游之间发现上述代码的精简版

 private void bubbleSort4(int A[], int n) {
        for (boolean sorted = false; sorted = !sorted; n--) {
            for (int i = 1; i < n; i ++) {
                if (A[i-1] > A[i]) {
                    int temp = A[i-1];
                    A[i-1] = A[i];
                    A[i] = temp;
                    sorted = false;
                }
            }
        }
    }

优化2

 ////改进思路:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok
    public static void bubbleSort2(int [] num, int length) {
        int last = length;
        int k;
        while (last > 0) {
            k = last;
            last = 0;

            for (int j = 1; j < k; j ++) {
                if (num[j - 1] > num[j]) {
                    int temp = num[j - 1];
                    num[j - 1] = num[j];
                    num[j] = temp;
                    last = j;
                }
            }
        }
    }

算法正确性的证明

问题: 该算法是否一定会终止(有穷性)? 至少迭代多少趟呢?

分析

这里写图片描述

我们知道:
该算法具有

  1. 不变性。经k轮扫描交换,最大的k个元素必然会就位。
  2. 单调性。经k轮扫描交换,问题规模缩减为n-k;

    通过这两条性质的挖掘综合,则得出算法的正确性,经过至少n趟扫描后,算法必然终止, 且给出正确答案

参考

数据结构–邓俊辉

冒泡排序

[置顶] 十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值