冒泡排序算法 以及两种优化方法 和性能比较分析 C++代码实现 恋上数据结构笔记

前言

其实犹豫了很久要不要建个个人博客,不用csdn了,但是想到各种太麻烦了,还是这里最方便
想写的都在代码注释里了,下一次复习应该会更新一个图解,算法真是比数据结构有意思多了
感谢peng1ei的算法时间工具,我这个小萌新表示很好用
以后遇到新的优化版本,还会加在这里

冒泡排序特性

  1. 是稳定(Stability)的排序算法(指【1,2(a),2(b),3】排序后两个2的位置不会对调)
  2. 是原地算法(In-place Algorithm),指不依赖额外的资源或仅依赖少数的额外资源,仅依靠输出来覆盖输入(空间复杂度低)

变量名解释

  1. compareTimes 单纯的比较次数计数器,没什么用,就为了看看三个算法比较次数的区别
  2. end 控制结束比较的数组下标
  3. i 比较和调换两个元素中,前面那个元素在数组中的下标
  4. alreadyAscend bool值,标志是否已经是有序序列

复习梗概

  1. 冒泡排序图解
  2. 基础版的两个循环结束条件?
  3. 第二次优化了哪里?怎么实现的
  4. 第三次优化了哪里?怎么实现的

冒泡排序基础版

void bubbleSort1th(vector<int> &array)
{
    int compareTimes = 0; //记录比较次数,为了体现三个算法的区别
    //这里需要特殊注意循环结束的条件
    //我的理解是外循环end值控制每次循环比较的次数,
    //联想第一次循环是从下标0开始与后一位比较,到下标[size -2]与下标[size -1]比较,所以总共是【size - 2 +1】次
    for (int end = array.size() - 1; end > 1; end--) //外循环控制每次比较结束的地方,因为后面内循环冒泡出的有序序列就不用再比较了
    {
        for (int i = 0; i < end; i++) //内循环控制进行两个相邻元素的比较
        {
            compareTimes++;
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
            }
        }
        vectorPrint(array);
    }
    cout << "共比较" << compareTimes << "次" << endl;
}

输入数组就是第一行那个,具体过程和结果以及比较次数和用时如下

6 9 6 7 5 8 8 29 15 11 10 
冒泡打印基础版
6 9 6 7 5 8 8 29 15 11 10
6 6 7 5 8 8 9 15 11 10 29
6 6 5 7 8 8 9 11 10 15 29
6 5 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
共比较54次
算法用时:(微秒)
[AlgoTime: 12003 us]

冒泡排序2nd优化版

void bubbleSort2nd(vector<int> &array) //冒泡排序的第一次优化,优化了提前有序的情况,但这种情况不多见,有时反倒负优化
{
    int compareTimes = 0;
    for (int end = array.size() - 1; end > 1; end--)
    {
        bool alreadyAscend = true; //增加了一个判断是否剩下的队列已经是升序队列
        for (int i = 0; i < end; i++)
        {
            compareTimes++;
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                alreadyAscend = false;
                //如果这一轮比较,但凡有一次调换位置,都有可能使队列不是有序,若一次调换都没有那说明已经是有序的了
            }
        }
        vectorPrint(array);
        if (alreadyAscend)
        {
            break;
        }
    }
    cout << "共比较" << compareTimes << "次" << endl;
}
6 9 6 7 5 8 8 29 15 11 10
冒泡打印优化版
6 6 7 5 8 8 9 15 11 10 29
6 6 5 7 8 8 9 11 10 15 29
6 5 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
共比较40次
算法用时:(微秒)
[AlgoTime: 7001 us]

冒泡排序3rd优化版

void bubbleSort3rd(vector<int> array)
{
    int compareTimes = 0;
    //冒泡排序的第二次优化,优化了重复比较的地方(包括了第二次优化的提前有序情况)
    //有时可能不明白这里优化了哪,比如1,2,4,3,5,6这么一个数组
    //第一次循环后【1,2,3,4,5,{6}】,此时虽然最后一次调换发生在4与3之间,但其实后面的5,6经过比较已经比前面最大的4还大,而且有序
    //所以,第二次循环就可以直接在2和3的比较处结束了,因为后面已经比前面大且有序了
    //而lastTempLocation就是记录这里结束的位置(上次调换的位置)
    for (int end = array.size() - 1; end > 0; 1)
    {
        int lastTempLocation = 0;
        //记录上一次比较的最后的元素的索引,此时后面的元素已经是有序的了
        //默认初始值为0,这样若数组已经完全有序,下一轮循环的end就为0,会直接退出循环
        for (int i = 0; i < end; i++)
        {
            compareTimes++;
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                lastTempLocation = i;
            }
        }
        end = lastTempLocation;
        vectorPrint(array);
        //虽然最后输出和第二种优化是打印遍数是一样的,但是实际上少了很多比较的过程
    }
    cout << "共比较" << compareTimes << "次" << endl;
}
6 9 6 7 5 8 8 29 15 11 10
冒泡打印第二次优化版
6 6 7 5 8 8 9 15 11 10 29
6 6 5 7 8 8 9 11 10 15 29 
6 5 6 7 8 8 9 10 11 15 29
5 6 6 7 8 8 9 10 11 15 29
共比较34次
算法用时:(微秒)
[AlgoTime: 6002 us]

全部代码

#include <iostream>
#include <vector>
#include "MeasureAlgoTime.hpp"
using namespace std;

void vectorPrint(vector<int> &array)
{
    for (int i = 0; i < array.size(); i++)
    {
        cout << array[i] << ' ';
    }
    cout << endl;
}

void bubbleSort1th(vector<int> &array)
{
    int compareTimes = 0; //记录比较次数,为了体现三个算法的区别
    //这里需要特殊注意循环结束的条件
    //我的理解是外循环end值控制每次循环比较的次数,
    //联想第一次循环是从下标0开始与后一位比较,到下标[size -2]与下标[size -1]比较,所以总共是【size - 2 +1】次
    for (int end = array.size() - 1; end > 1; end--) //外循环控制每次比较结束的地方,因为后面内循环冒泡出的有序序列就不用再比较了
    {
        for (int i = 0; i < end; i++) //内循环控制进行两个相邻元素的比较
        {
            compareTimes++;
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
            }
        }
        vectorPrint(array);
    }
    cout << "共比较" << compareTimes << "次" << endl;
}

void bubbleSort2nd(vector<int> &array) //冒泡排序的第一次优化,优化了提前有序的情况,但这种情况不多见,有时反倒负优化
{
    int compareTimes = 0;
    for (int end = array.size() - 1; end > 1; end--)
    {
        bool alreadyAscend = true; //增加了一个判断是否剩下的队列已经是升序队列
        for (int i = 0; i < end; i++)
        {
            compareTimes++;
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                alreadyAscend = false;
                //如果这一轮比较,但凡有一次调换位置,都有可能使队列不是有序,若一次调换都没有那说明已经是有序的了
            }
        }
        vectorPrint(array);
        if (alreadyAscend)
        {
            break;
        }
    }
    cout << "共比较" << compareTimes << "次" << endl;
}

void bubbleSort3rd(vector<int> array)
{
    int compareTimes = 0;
    //冒泡排序的第二次优化,优化了重复比较的地方(包括了第二次优化的提前有序情况)
    //有时可能不明白这里优化了哪,比如1,2,4,3,5,6这么一个数组
    //第一次循环后【1,2,3,4,5,{6}】,此时虽然最后一次调换发生在4与3之间,但其实后面的5,6经过比较已经比前面最大的4还大,而且有序
    //所以,第二次循环就可以直接在2和3的比较处结束了,因为后面已经比前面大且有序了
    //而lastTempLocation就是记录这里结束的位置(上次调换的位置)
    for (int end = array.size() - 1; end > 0; 1)
    {
        int lastTempLocation = 0;
        //记录上一次比较的最后的元素的索引,此时后面的元素已经是有序的了
        //默认初始值为0,这样若数组已经完全有序,下一轮循环的end就为0,会直接退出循环
        for (int i = 0; i < end; i++)
        {
            compareTimes++;
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                lastTempLocation = i;
            }
        }
        end = lastTempLocation;
        vectorPrint(array);
        //虽然最后输出和第二种优化是打印遍数是一样的,但是实际上少了很多比较的过程
    }
    cout << "共比较" << compareTimes << "次" << endl;
}

int main()
{
    Tools::Time::AlgoTimeUs time1;
    Tools::Time::AlgoTimeUs time2;
    Tools::Time::AlgoTimeUs time3;

    vector<int> array;
    array = {1, 2, 3, 4, 5, 8, 7, 6, 9, 10, 11, 15, 29};
    vector<int> array2 = array;
    vector<int> array3 = array;

    time1.start();
    vectorPrint(array);
    cout << "冒泡打印基础版" << endl;
    vectorPrint(array);
    bubbleSort1th(array);
    cout << "算法用时:(微秒)";
    time1.printElapsed();
    cout << ' ' << endl;

    time2.start();
    vectorPrint(array2);
    cout << "冒泡打印优化版" << endl;
    bubbleSort2nd(array2);
    cout << "算法用时:(微秒)";
    time2.printElapsed();
    cout << ' ' << endl;

    time3.start();
    vectorPrint(array3);
    cout << "冒泡打印第二次优化版" << endl;
    bubbleSort3rd(array3);
    cout << "算法用时:(微秒)";
    time3.printElapsed();

    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值