思想
先来看一张动图
上面这张图就是冒泡排序的代码可视化
很显然我们可以发现,冒泡排序的基本思想就是从前往后比对,一直将找到的最大值交换到序列的末尾感觉冒泡排序这个名字还是很形象的
朴素做法
不难看出,将最大值交换到末尾的操作一共要进行n次,需要比对的元素也有n个,所以就可以使用双层循环实现
来看一下伪代码
for(i=1...n){
for(j=i..n){
if(a[j]>a[j+1]){
交换a[j]和a[j+1]的值;
}
}
}
Code
交换操作的实现
交换操作有两种实现方式我太弱了只知道两种
第一种:swap函数
大爱math库
swap(a[j],a[j+1]);
第二种:手搓
通过一个中间变量t来交换两个值
int t = a[j];
a[j]=a[j+1];
a[j+1]=t;
感觉有点难理解的可以思考一下如果你有两杯水,给你一个空杯子,如何交换两个杯子里的水
朴实无华的 O ( n 2 ) O(n^{2}) O(n2)无任何优化的代码
#include <bits/stdc++.h>//万能头(不用管这是什么)
using namespace std;
int a[10] = {5,6,7,4,2,3,1,0} ;
int main(){
for(int i = 0; i < 8;i++){
for(int j = 0; j < 8-1; j++){//因为是和j+1比较,所以不遍历到最后一个元素
if(a[j] > a[j+1]){
swap(a[j],a[j+1]);
}
}
}
for(int i = 0; i < 8; i++){
cout << a[i] << " ";
}
return 0;
}
输出
0 1 2 3 4 5 6 7
第一次优化
这个排序的名称为什么说叫冒泡呢,因为每一次循环结束都会有一个最大值被排到最后面,也就是说在第
i
i
i次循环结束之后,最后
i
i
i个数是有序的
那么我们是不是就可以将后面有序的部分踢出第二层循环
Code
#include <bits/stdc++.h>//万能头(不用管这是什么)
using namespace std;
int a[10] = {5,6,7,4,2,3,1,0} ;
int main(){
for(int i = 0; i < 8;i++){
for(int j = 0; j < 8-i-1; j++){//最后i个数已经有序,不参与排序
if(a[j] > a[j+1]){
swap(a[j],a[j+1]);
}
}
}
for(int i = 0; i < 8; i++){
cout << a[i] << " ";
}
return 0;
}
最终优化
当我们模拟排序过程的时候,我们会发现有些时候排序未结束但是数组已经有序了
所以我们可以想办法进行判断数组是否已经有序,若有序就直接结束循环
如何判断是否有序
思考可以发现,如果一个数组是有序的,那么在冒泡排序时就不会进行交换操作
那么我们可以设置一个flag变量来判断有没有进行交换
Code
#include <bits/stdc++.h>//万能头(不用管这是什么)
using namespace std;
int a[10] = {5,6,7,4,2,3,1,0} ;
int main(){
for(int i = 0; i < 8;i++){
bool flag = 1;//这里一定要定义在第一层循环内部
for(int j = 0; j < 8-i-1; j++){//最后i个数已经有序,不参与排序
if(a[j] > a[j+1]){
swap(a[j],a[j+1]);
flag = 0;
}
}
if(flag) break;
}
for(int i = 0; i < 8; i++){
cout << a[i] << " ";
}
return 0;
}
来看一道例题
题目传送门
其实就是冒泡排序的板子题,输出交换的次数就OK