冒泡排序

综述

冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来.
走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端.

算法描述

1.比较相邻的元素.如果前一个比后二个大,就交换它们两个;
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3.针对所有的元素重复以上的步骤,除了最后一个;
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较.

示意图

动态示意图
静态示意图

性质

排序类别:交换排序
是否是稳定排序:稳定
是否是原地排序:是
时间复杂度:O(N^2)
空间复杂度:O(1)


要点

冒泡排序是一种交换排序.
交换排序:两两比较待排序的关键字,并交换不满足次序要求的那对数,直到整个表都满足次序要求为止.

算法思想

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,故名冒泡排序.

假设有一个大小为 N 的无序序列.冒泡排序就是要每趟排序过程中通过两两比较,找到第 i 个小(大)的元素,将其往上排.
要将以上流程转化为代码,我们需要像机器一样去思考,不然编译器可看不懂.
假设要对一个大小为 N 的无序序列进行升序排序(即从小到大).
(1)每趟排序过程中需要通过比较找到第 i 个小的元素.
所以,我们需要一个外部循环,从数组首端(下标 0)开始,一直扫描到倒数第二个元素(即下标 N - 2),剩下最后一个元素,必然为最大.
(2)假设是第 i 趟排序,可知,前 i-1 个元素已经有序.现在要找第 i 个元素,只需从数组末端开始,扫描到第 i 个元素,将它们两两比较即可.
所以,需要一个内部循环,从数组末端开始(下标 N - 1),扫描到(下标 i + 1).

时间复杂度分析

1.若文件的初始状态是正序的,一趟扫描即可完成排序.所需的关键字比较次数C和记录移动次数M均达到最小值:Cmin=N-1, Mmin=0.所以,冒泡排序最好时间复杂度为O(N).
2.若初始文件是反序的,需要进行 N -1 趟排序.每趟排序要进行(N-i)次关键字的比较(1 ≤ i ≤ N - 1),且每次比较都必须移动记录三次来达到交换记录位置.
在这种情况下,比较和移动次数均达到最大值:
Cmax = N(N-1)/2 = O(N^2)
Mmax = 3N(N-1)/2 = O(N^2)
冒泡排序的最坏时间复杂度为O(N^2).

因此,冒泡排序的平均时间复杂度为O(N^2).

总结起来,其实就是一句话:当数据越接近正序时,冒泡排序性能越好.

稳定性分析

冒泡排序就是把小的元素往前调或者把大的元素往后调.比较是相邻的两个元素比较,交换也发生在这两个元素之间.
所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法.

优化方式

优化一

假设我们现在排序ar[]={1,2,3,4,5,6,7,8,10,9}这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,什么也没做.所以我们可以在交换的地方加一个标记,如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去.

优化二

优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3,4,7,6,5).但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化:即我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可.

优化三

优化二的效率有很大的提升,还有一种优化方法可以继续提高效率.大致思想就是一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面.
例如:排序数据1,2,3,4,5,6,0

Python实现及优化

def bubblesort1(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    for i in range(len(dest_list) - 1):
        print('第{}趟排序为:'.format(str(i + 1)))
        for j in range(len(dest_list) - 1 - i):
            print('第{}次排序为:'.format(str(j + 1)), dest_list)
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
    return dest_list

dest = [5, 3, 4, 6, 2, 1, 7]
result = bubblesort1(dest)
print('最后的结果是:', result)

'''
第1趟排序为:
第1次排序为: [5, 3, 4, 6, 2, 1, 7]
第2次排序为: [3, 5, 4, 6, 2, 1, 7]
第3次排序为: [3, 4, 5, 6, 2, 1, 7]
第4次排序为: [3, 4, 5, 6, 2, 1, 7]
第5次排序为: [3, 4, 5, 2, 6, 1, 7]
第6次排序为: [3, 4, 5, 2, 1, 6, 7]
第2趟排序为:
第1次排序为: [3, 4, 5, 2, 1, 6, 7]
第2次排序为: [3, 4, 5, 2, 1, 6, 7]
第3次排序为: [3, 4, 5, 2, 1, 6, 7]
第4次排序为: [3, 4, 2, 5, 1, 6, 7]
第5次排序为: [3, 4, 2, 1, 5, 6, 7]
第3趟排序为:
第1次排序为: [3, 4, 2, 1, 5, 6, 7]
第2次排序为: [3, 4, 2, 1, 5, 6, 7]
第3次排序为: [3, 2, 4, 1, 5, 6, 7]
第4次排序为: [3, 2, 1, 4, 5, 6, 7]
第4趟排序为:
第1次排序为: [3, 2, 1, 4, 5, 6, 7]
第2次排序为: [2, 3, 1, 4, 5, 6, 7]
第3次排序为: [2, 1, 3, 4, 5, 6, 7]
第5趟排序为:
第1次排序为: [2, 1, 3, 4, 5, 6, 7]
第2次排序为: [1, 2, 3, 4, 5, 6, 7]
第6趟排序为:
第1次排序为: [1, 2, 3, 4, 5, 6, 7]
最后的结果是: [1, 2, 3, 4, 5, 6, 7]
'''

"""
优化方式一
对冒泡排序常见的改进方法是加入标志性变量exchange,用于标志某一趟排序过程中是否有数据交换。
如果进行某一趟排序时并没有进行数据交换,则说明所有数据已经有序,可立即结束排序,避免不必要的比较过程。
"""


def bubblesort2(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    for i in range(len(dest_list)):
        exchange = False
        print('第{}趟排序:'.format(str(i + 1)))
        for j in range(len(dest_list) - 1 - i):
            print('第{}次排序为:'.format(str(j + 1)), dest_list)
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
                exchange = True
        if not exchange:
            break

    return dest_list

dest = [2, 1, 4, 3, 5, 6, 7]
result = bubblesort2(dest)
print('最后的结果是:', result)

'''
第1趟排序:
第1次排序为: [2, 1, 4, 3, 5, 6, 7]
第2次排序为: [1, 2, 4, 3, 5, 6, 7]
第3次排序为: [1, 2, 4, 3, 5, 6, 7]
第4次排序为: [1, 2, 3, 4, 5, 6, 7]
第5次排序为: [1, 2, 3, 4, 5, 6, 7]
第6次排序为: [1, 2, 3, 4, 5, 6, 7]
第2趟排序:
第1次排序为: [1, 2, 3, 4, 5, 6, 7]
第2次排序为: [1, 2, 3, 4, 5, 6, 7]
第3次排序为: [1, 2, 3, 4, 5, 6, 7]
第4次排序为: [1, 2, 3, 4, 5, 6, 7]
第5次排序为: [1, 2, 3, 4, 5, 6, 7]
最后的结果是: [1, 2, 3, 4, 5, 6, 7]
'''

"""
优化方式二
优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3 ,4 ,7,6,5)。
但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于这种类型数据,我们可以继续优化。
我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。
"""


def bubblesort3(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    k = len(dest_list) - 1  # 确定比较次数
    for i in range(len(dest_list)):
        exchange = False
        pos = 0  # 用来记录最后一次交换的位置
        print('第{}趟排序:'.format(str(i + 1)))
        for j in range(k):
            print('第{}次排序为:'.format(str(j + 1)), dest_list)
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
                exchange = True
                pos = j  # 交换元素,记录最后一次交换的位置
        if not exchange:  # 如果没有交换过元素,则已经有序
            break
        k = pos  # 下一次比较到记录位置即可

    return dest_list

dest = [2, 1, 4, 3, 5, 6, 7]
result = bubblesort3(dest)
print('最后的结果是:', result)

'''
第1趟排序:
第1次排序为: [2, 1, 4, 3, 5, 6, 7]
第2次排序为: [1, 2, 4, 3, 5, 6, 7]
第3次排序为: [1, 2, 4, 3, 5, 6, 7]
第4次排序为: [1, 2, 3, 4, 5, 6, 7]
第5次排序为: [1, 2, 3, 4, 5, 6, 7]
第6次排序为: [1, 2, 3, 4, 5, 6, 7]
第2趟排序:
第1次排序为: [1, 2, 3, 4, 5, 6, 7]
第2次排序为: [1, 2, 3, 4, 5, 6, 7]
最后的结果是: [1, 2, 3, 4, 5, 6, 7]
'''

"""
优化方式三
大致思想就是一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面。
"""


def bubblesort4(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    k = len(dest_list) - 1  # 确定比较次数
    n = 0  # 同时找最大值的最小需要两个下标遍历
    for i in range(len(dest_list)):
        exchange = False
        pos = 0  # 用来记录最后一次交换的位置
        print('第{}趟排序:'.format(str(i + 1)))
        for j in range(n, k):
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
                exchange = True
                pos = j  # 交换元素,记录最后一次交换的位置
            print('第{}次排序为:'.format(str(j + 1)), dest_list)
        if not exchange:  # 如果没有交换过元素,则已经有序
            break
        k = pos  # 下一次比较到记录位置即可

        # print('进行倒序寻找...')

        for m in range(k, n, -1):
            if dest_list[m] < dest_list[m - 1]:
                dest_list[m], dest_list[m - 1] = dest_list[m - 1], dest_list[m]
                exchange = True
            # print('第{}次排序为:'.format(str(k - n - m + 1)), dest_list)
        n += 1
        if not exchange:  # 如果没有交换过元素,则已经有序
            break

    return dest_list

dest = [1, 2, 3, 4, 5, 6, 0]
result = bubblesort4(dest)
print('最后的结果是:', result)


'''
第1趟排序:
第1次排序为: [1, 2, 3, 4, 5, 6, 0]
第2次排序为: [1, 2, 3, 4, 5, 6, 0]
第3次排序为: [1, 2, 3, 4, 5, 6, 0]
第4次排序为: [1, 2, 3, 4, 5, 6, 0]
第5次排序为: [1, 2, 3, 4, 5, 6, 0]
第6次排序为: [1, 2, 3, 4, 5, 0, 6]
第2趟排序:
第2次排序为: [0, 1, 2, 3, 4, 5, 6]
第3次排序为: [0, 1, 2, 3, 4, 5, 6]
第4次排序为: [0, 1, 2, 3, 4, 5, 6]
第5次排序为: [0, 1, 2, 3, 4, 5, 6]
最后的结果是: [0, 1, 2, 3, 4, 5, 6]
'''

C语言实现及优化

#include <stdio.h>


/* 1.从当前元素起,向后依次比较每一对相邻元素,若逆序则交换 */
/* 2.对所有元素均重复以上步骤,直至最后一个元素 */
void bubbleSort(int arr[],int len)
{
    int i = 0;
    int count = 0;
    int tmp = 0;
    for(i = 0; i < len - 1; i++)//确定排序趟数
    {
        int j = 0;
        for(j = 0; j < len - 1 - i; j++)//确定比较次数
        {
            if(arr[j]>arr[j + 1])
            {
                //交换
                tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                count ++;
            }
        }
    }
    printf("最后交换的次数是:%d\n", count);
}

/*
优化一
假设我们现在排序ar[]={1,2,3,4,5,6,7,8,10,9}这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,什么也没做.
所以我们可以在交换的地方加一个标记,如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去.
*/

void bubbleSort2(int arr[], int len)
{
    int i=0, j=0, count=0;
    for(i=0;i<len;i++)             /* 外循环为排序趟数,len个数进行len-1趟 */
    {
        int flag = 0;
        for(j=0;j<len-i-1;j++)     /* 内循环为每趟比较的次数,第i趟比较len-i次 */
        {
            if(arr[j]>arr[j+1])    /* 相邻元素比较,若逆序则交换(升序为左大于右,降序反之)*/
            {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag = 1;
                count ++;
            }
        }

        if(flag)
            printf("最后交换的次数是:%d\n", count);
            return;
    }
    printf("最后交换的次数是:%d\n", count);
}

/*
优化二
优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3,4,7,6,5).
但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化.
既我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可.
*/
void bubbleSort3(int arr[], int len)
{
    int i = 0;
    int count = 0;
    int tmp = 0;
    int flag = 0;
    int pos = 0;//用来记录最后一次交换的位置
    int k = len - 1;
    for(i = 0; i < len - 1; i++)//确定排序趟数
    {
        pos = 0;
        int j = 0;
        flag = 0;
        for(j = 0; j < k; j++)//确定比较次数
        {
            if(arr[j]>arr[j + 1])
            {
                //交换
                tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 1;//加入标记
                pos = j;//交换元素,记录最后一次交换的位置
                count ++;
            }
        }

        if(flag == 0)//如果没有交换过元素,则已经有序
        {
            printf("最后交换的次数是:%d\n", count);
            return;
        }
        k = pos;//下一次比较到记录位置即可
    }
    printf("最后交换的次数是:%d\n", count);
    return;
}


/*
优化三
优化二的效率有很大的提升,还有一种优化方法可以继续提高效率.
大致思想就是一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面.例如:排序数据1,2,3,4,5,6,0
*/

void bubbleSort4(int arr[], int len)
{
    int i = 0;
    int j = 0;
    int n = 0;//同时找最大值的最小需要两个下标遍历
    int flag = 0;
    int pos = 0;//用来记录最后一次交换的位置
    int k = len - 1;
    for(i = 0; i < len - 1; i++)//确定排序趟数
    {
        pos = 0;
        flag = 0;
        //正向寻找最大值
        for(j = n; j < k; j++)//确定比较次数
        {
            if(arr[j]>arr[j + 1])
            {
                //交换
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 1;//加入标记
                pos = j;//交换元素,记录最后一次交换的位置
            }
        }
        if(flag == 0)//如果没有交换过元素,则已经有序,直接结束
        {
            return;
        }
        k = pos;//下一次比较到记录位置即可
        //反向寻找最小值
        for(j = k; j > n; j--)
        {
            int tmp = arr[j];
            arr[j] = arr[j - 1];
            arr[j - 1] = tmp;
            flag = 1;
        }
        n++;
        if(flag == 0)//如果没有交换过元素,则已经有序,直接结束
        {
            return;
        }
    }
}




int main()
{
    int i=0;

    int dest[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort(dest, 7);
    for(i=0;i<7;i++)
        printf("%d ", dest[i]);
    printf("\n");

    int dest1[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort2(dest1, 7);
    for(i=0;i<7;i++)
        printf("%d ", dest1[i]);
    printf("\n");

    int dest2[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort3(dest2, 10);
    for(i=0;i<10;i++)
        printf("%d ", dest2[i]);
    printf("\n");

    int dest3[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort4(dest2, 10);
    for(i=0;i<10;i++)
        printf("%d ", dest3[i]);
    printf("\n");

    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,622评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,716评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,746评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,991评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,706评论 6 413
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,036评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,029评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,203评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,725评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,451评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,677评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,161评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,857评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,266评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,606评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,407评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,643评论 2 380

推荐阅读更多精彩内容