时间复杂度:最好O(n) 最坏(序列为逆序)O(n²) 平均O(n²)
空间复杂度:O(1)
稳定性:稳定
大部分人会写冒泡排序,概念也是笼统的听了一下并没有清晰的了解原理
这篇文章将讲解一下冒泡排序的各个值和范围的含义
首先看一下代码:
//冒泡排序基础版本
//i的含义:目前后面有序元素的数量
//[j < arr.size()-i-1] 的含义:比较到后面有序元素之前的元素
// -1是因为比如3个数需要比较两次,5个数需要比较四次
void bubbleSort(vector<int>& arr) {
for (int i = 0; i < arr.size(); i++) {
for (int j = 0; j < arr.size() - i-1; j++) {
if (arr[j] > arr[j + 1]) {
//交换
arr[j] ^= arr[j + 1];
arr[j+1] ^= arr[j];
arr[j] ^= arr[j + 1];
}
}
}
}
首先冒泡排序的核心思想就是: 将元素最大/小的依次放到最后面。
我们需要对所有元素进行排序没走一趟都会将最大/小值放在后面, 那么 i 在算法中的含义可以理解为后面有序元素的个数。
在内层循环比较中, 为什么比较的次数是 arr.size() - i-1 是因为:
首先 -i 是减去了后面有序元素的个数, 因为已经有序的元素就是已经确定了的, 不需要再次参与比较。
-1 是因为, 比如3个数需要比较两次,5个数需要比较四次, 那么 arr.size() - i 个元素就需要 arr.size() - i-1 次。
那么既然i代表后续有序元素的个数,那么是不是有序一定是一个一个放在后面的?
显然是不是的,那么我们怎么判断有序元素的范围,其实在冒泡排序中每次遇到不符合排序规则的元素就会发生交换,那么我们记录一下最后交换元素的位置,那他的后面就都是符合排序规则的有序元素。
void bubbleSort2(vector<int>& arr) {
int flag;
for (int i = 0; i < arr.size(); i++) {
flag = 0;//标记最后一次交换的位置
for (int j = 0; j < arr.size() - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
//交换
arr[j] ^= arr[j + 1];
arr[j + 1] ^= arr[j];
arr[j] ^= arr[j + 1];
flag = j + 1;
}
}
//flag == 0 表示发生0次交换即数组已经有序,退出
if (flag == 0)break;
//更新i(有序元素的个数) -1是因为下一次循环会+1
i = arr.size() - flag - 1;
}
}
使用一个标记变量flag来标记最后一次交换的位置,每次走内循环设置flag=0,走完内层循环都更新一下 i 为 arr.size() - flag - 1 ,i 既然代表的是后续有序元素的数量,那么为什么还要-1?
因为循环结束会i++。
这样就对冒泡排序进行了优化。