图解冒泡排序

生来就可爱ヽ(ⅴ<●) 提交于 2021-01-10 08:34:08

冒泡排序

冒泡排序(Bubble Sort),是一种简单的排序算法。
算法名字由来:越小的元素会经由交换慢慢 "浮" 到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

算法重复遍历要排序的元素列,依次比较两个相邻的元素,如下图所示。如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。遍历元素的工作是重复的进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

冒泡排序有两种代码实现方式:

  • 将最小的元素 出来
  • 将最大的元素 下去

冒出最小元素

执行过程如下:

  • 从后往前,依次比较两个相邻元素
  • 如果 前面的元素 比 后面元素大,则交换两个元素
  • 对每一对相邻元素做同样的工作,从最后一对到开始的第一对
  • 当第一遍执行完后,第一个元素就是最小的元素
  • 针对所有的的元素重复上述步骤,除了第一个
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

[4,7,2,5,1,3]为例,将最小的元素出来的示意图如下
从后往前依次比较两个相邻元素,数组中最小的元素是1
每次比较过后,1都会被交换到更靠前的位置 等到第5步过后,数组中的第一个元素就是最小的元素了。

还是以[4,7,2,5,1,3]为例,完整的排序过程如下:上图中

  • 第一趟遍历后,我们将最小的元素 出到第一位,也就是排到了数组的第一位
  • 第二趟后,第二小的元素被排到了数组的第二位
  • 第三趟后,第三小的元素被排到了数组的第三位
  • 第四趟后,第四小的元素被排到了数组的第四位
  • 第五趟后,第五小的元素被排到了数组的第五位

这就是冒泡排序的特点,每次都把最小的元素往前挪。
由于上面数组一共有6个元素,需要5趟比较,所以对于有N个元素的数组来说,需要比较N-1趟。

数组[4,7,2,5,1,3],完整的排序动画如下图所示:

java代码:

public class BubbleSort {
    public void sort(int[] arr) {
        if(arr==null || arr.length==0) {
            return;
        }
        int n = arr.length;
        //外层指定了需要排序多少趟,N个元素需要N-1趟排序
        for(int i=0;i<n-1;++i) {
            //从最后一个元素一直比较到第i个元素,初始的时候i=0
            //第一趟后第一个元素就是最小的,
            //之后i=1即第二趟后,第二个元素是第二小的
            //i=3,第三趟后第三个元素第三小
            for(int j=n-1;j>i;--j) {
                //如果前面的元素比后面的大,就交换两个元素
                if(arr[j-1] > arr[j]) {
                    int tmp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j-1] = tmp;
                }
            }
        }
    }
}

python代码:

class BubbleSort(object):
    def sort(self,arr):
        if not arr:
            return
        n = len(arr)
        # 外层指定了需要排序多少趟,N个元素需要N-1趟排序
        for i in xrange(n-1):
            # 从最后一个元素一直比较到第i个元素,初始的时候i=0
            # 第一趟后第一个元素就是最小的,
            # 之后i=1即第二趟后,第二个元素是第二小的
            # i=3,第三趟后第三个元素第三小
            for j in xrange(n-1,i,-1):
                # 如果前面的元素比后面的大,就交换两个元素
                if arr[j] < arr[j-1]:
                    arr[j],arr[j-1] = arr[j-1],arr[j]
                    

沉下最大元素

沉下最大元素和冒出最小元素很类似,冒出最小是将小元素往前挪动,而沉下最大元素是将大元素往后挪动。
也就是将最大的元素挪动到数组的后面

执行过程如下:

  • 从前往后,依次比较两个相邻元素
  • 如果 前面的元素 比 后面元素大,则交换两个元素
  • 对每一对相邻元素做同样的工作,从开始的第一对到最后一对
  • 当第一遍执行完后,最后一个元素就是最大的元素
  • 针对所有的的元素重复上述步骤,除了最后一个
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

[4,7,2,5,1,3]为例,将最大的元素下去的示意图如下
从前往后依次比较两个相邻元素,数组中最大的元素是7
每次比较过后,7都会被交换到更靠后的位置 等到第5步过后,数组中的最后一个元素就是最大的元素了。

数组[4,7,2,5,1,3],完整的排序动画如下图所示:

上图中

  • 第一趟遍历后,我们将最大的元素 到到最后一位,也就是排到了数组的末尾
  • 第二趟后,第二大的元素被排到了数组的倒数第二位
  • 第三趟后,第三大的元素被排到了数组的倒数第三位
  • 第四趟后,第四大的元素被排到了数组的倒数第四位
  • 第五趟后,第五大的元素被排到了数组的倒数第五位

[4,7,2,5,1,3]这个数组,完整的排序动画如下图所示:

java代码:

public class BubbleSort {
    public void sort(int[] arr) {
        if(arr==null || arr.length==0) {
            return;
        }
        int n = arr.length;
        //外层指定了需要排序多少趟,N个元素需要N-1趟排序
        for(int i=0;i<n-1;++i) {
            //从第一个元素一直比较到第n-1个元素,第一趟j<n-1
            //第二趟,从第一个元素比较到n-2个元素,j<n-2
            //第三趟,从第一个元素比较到n-3个元素,j<n-3
            for(int j=0;j<n-i-1;++j) {
                //如果前面的元素比后面的大,就交换两个元素
                if(arr[j] > arr[j+1]) {
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                }
            }
        }
    }
}

python代码:

class BubbleSort(object):
    def sort(self):
        if not arr:
            return
        n = len(arr)
        # 外层指定了需要排序多少趟,N个元素需要N-1趟排序
        for i in xrange(n-1):
            # 从第一个元素一直比较到第n-1个元素,第一趟j<n-1
            # 第二趟,从第一个元素比较到n-2个元素,j<n-2
            # 第三趟,从第一个元素比较到n-3个元素,j<n-3
            for j in xrange(n-i-1):
                # 如果前面的元素比后面的大,就交换两个元素
                if arr[j] > arr[j+1]:
                    arr[j],arr[j+1] = arr[j+1],arr[j]
                    

细节分析和优化

无论是冒出最小元素,还是沉下最大元素,都需要执行N-1趟比较,也就是外层循环是执行N-1次。
这里的N是数组长度。
外层循环需要执行N-1次,内层循环是受到外层循环控制的

  • 第一趟会执行 N-1次比较,这之后数组中第一个元素就是最小的了
  • 第二趟需要执行 N-2次比较,因为最小的元素已经找到了,我们需要找第二小的,第二趟后数组中第二个元素是最小的
  • 第三趟需要执行 N-3次比较
  • 最后一趟需要执行 1次比较

冒泡排序最坏的情况下需要执行N-1趟,每趟比较N-i次,(1≤i≤n-1)
每次比较要移动记录三次来达到交换记录位置
所以,比较(C)的次数,移动(M)的次数分别为:

冒泡排序的最坏时间复杂度为 O(N^2)
冒泡排序总的平均时间复杂度为O(N^2)
由于没有用到其他辅助空间,空间复杂度为O(1)

对于一个完全有序的数组,上述代码仍然需要执行O(N^2)次
这样就完全没必要了,我们可以增加一个优化,如果一趟下来如果没有发生任何元素交换,那么就说明整个数组是有序的,可以直接退出循环。
这样当输入一个有序的数组时,冒泡排序的时间复杂度就是 O(N)

优化后的代码如下
java代码:

public class BubbleSort {
    public void sort(int[] arr) {
        if(arr==null || arr.length==0) {
            return;
        }
        int n = arr.length;
        for(int i=0;i<n-1;++i) {
            //定义一个是否交换的变量,如果内层循环中没有出现过交换
            //说明整个数组都是有序的,直接退出外层循环即可
            boolean isSwap = false;
            for(int j=n-1;j>i;--j) {
                if(arr[j-1] > arr[j]) {
                    int tmp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j-1] = tmp;
                    isSwap = true;
                }
            }
            //如果没有发生过交换,说明整个数组是有序的,直接退出外层循环 
            if(!isSwap) {
                break;
            }
        }
    }
}

python代码:

class BubbleSort(object):
    def sort(self):
        if not arr:
            return
        n = len(arr)
        for i in xrange(n-1):
            # 定义一个是否交换的变量,如果内层循环中没有出现过交换
            # 说明整个数组都是有序的,直接退出外层循环即可
            isSwap = False
            for j in xrange(n-i-1):
                if arr[j] > arr[j+1]:
                    arr[j],arr[j+1] = arr[j+1],arr[j]
                    isSwap = True
            # 如果没有发生过交换,说明整个数组是有序的,直接退出外层循环  
            if not isSwap:
                break

参考:

排序算法可视化:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

推荐阅读   点击标题可跳转

移动零

颜色分类

合并两个有序数组


看完本文有收获?请转发分享给更多人

好文章,我在看❤️

本文分享自微信公众号 - 大话算法(algorithm2pic)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!