冒泡排序及其优化


​ 我们总是有这种想法:当碰到一组数据时,我们总喜欢对这组数据进行排序。因为排好序后,我们可以轻松的进行分析。

​ 今天,我们来学习的排序算法是最简单的一种:冒泡排序。

​ 为什么叫冒泡排序呢?因为这种排序算法的每一个元素都可以像小气泡一样,根据自身的大小,一步步向数组的一侧移动。

冒泡排序第一轮排序

​ 我们可以看到,这第一轮中的9,一直慢慢的“飘到”数组的最后,这就是为什么这种算法被称为的冒泡排序的原因之一。同时,我们观察到,他是两两逐个比较的,一旦一个元素比他的右边的元素要大,就交换两者的位置,如果是小于或者等于就不做变动。

​ 下面是从网络上找到的一个冒泡排序的完整图:

冒泡排序示意图

既然知道了冒泡排序的过程,我们来看看冒泡排序的代码实现:

void BubbleSort1(int array[], int len)
{
    int i, j;

    for (i = 0; i < len - 1; i++) {
        for (j = 0; j < len - i - 1; j++) {
            if (array[j] > array[j+1]) {
                int temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
            }
        }
    }
}

​ 因为冒泡排序是两两相互比较,所以对于有n个元素的数组,我们只需要进行n-1次循环,就可以把他排好序。然后我们每一轮排序都可以找到一个最大的数,把他“归位”,所以在下一次比较中,我们只需比较元素个数减去轮次即可,即代码中的 j < len - i - 1。

​ 就这样,冒泡排序就完成了,他是一个很简单的排序算法,可以说其他的很多算法都是由冒泡排序演变过来的,比如快速排序,归并排序,堆排序。

​ 但是,不知道你发现了没有,当数组其实已经排好序了,但是我们的程序还不知道,所以他还是进行了两两比较的过程。

​ 为了解决这一点,我们可以设置一个标志符号,用来判断,这一轮的排序是否继续了交换,代码如下:

void BubbleSort2(int array[], int len)
{
    int i, j;

    for (i = 0; i < len - 1; i++) {
        int isChange = 1;
        for (j = 0; j < len - i - 1; j++) {
            if (array[j] > array[j+1]) {
                int temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
                isChange = 0;
            }
        }

        if (isChange) break;
    }
}

​ 但是,我们还发现了一个问题,什么问题呢?看下图:

冒泡排序03

​ 元素44,46,47明明已经排好序了,而冒泡排序还是傻傻的将他们进行了比较。我们思考一下,该如何解决这个问题呢?

​ 其实,我们可以记录最后一次发生交换的位置,然后下一轮排序的边界就是这个最后一次发生交换的下标。为什么呢?因为只要这一轮排序还没有到达这一轮排序的末尾边界,他又从这一轮排序中某个位置后,没有发生交换的过程。等到下一次排序碰到这个位置后的数字的时候,是不会发生交换的。这就说明这个最后一次发生交换的元素后面的其他所有元素,都是已经排好序的了。

​ 看起来有点复杂,我们不妨想一想,这一次我们遇到44、46、47的时候我们是没有发生交换的过程的。等这一轮排序结束后,47就已经归位了。下一轮,到44、46的时候,我们又比较了一次,但是他们没有发生交换,也就是说,我们在这里多比较了几次。我们优化的思路就是来源于这。

​ 试思考代码:

void BubbleSort3(int array[], int len)
{
    int i, j;
    int lastChangIndex = 0;
    int sortBorder = len - 1;

    for (i = 0; i < len - 1; i++) {
        int isChange = 1;
        for (j = 0; j < sortBorder; j++) {
            if (array[j] > array[j+1]) {
                int temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
                isChange = 0;
                lastChangIndex = j;
            }
        }
        sortBorder = lastChangIndex;
        if (isChange) break;
    }
}

​ 这里我们记录每轮排序最后一次发生交换的位置sortBorder,因为我们的算法是,每次找到最大的元素,然后把他“固定”好,所以,在sortBorder后面的元素一定是排好序的了。

​ 本文到这里就结束了,希望对你有帮助,谢谢!


Author: XiaoXiao
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source XiaoXiao !
评论
  TOC