前言
其实犹豫了很久要不要建个个人博客,不用csdn了,但是想到各种太麻烦了,还是这里最方便
想写的都在代码注释里了,下一次复习应该会更新一个图解,算法真是比数据结构有意思多了
感谢peng1ei的算法时间工具,我这个小萌新表示很好用
以后遇到新的优化版本,还会加在这里
冒泡排序特性:
- 是稳定(Stability)的排序算法(指【1,2(a),2(b),3】排序后两个2的位置不会对调)
- 是原地算法(In-place Algorithm),指不依赖额外的资源或仅依赖少数的额外资源,仅依靠输出来覆盖输入(空间复杂度低)
变量名解释:
- compareTimes 单纯的比较次数计数器,没什么用,就为了看看三个算法比较次数的区别
- end 控制结束比较的数组下标
- i 比较和调换两个元素中,前面那个元素在数组中的下标
- alreadyAscend bool值,标志是否已经是有序序列
复习梗概:
- 冒泡排序图解
- 基础版的两个循环结束条件?
- 第二次优化了哪里?怎么实现的
- 第三次优化了哪里?怎么实现的
冒泡排序基础版
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;
}