📕 算法介绍
冒泡排序是计算机科学领域比较简单的排序算法之一,因此也成为了java、Python、C++等各大编程语言算法入门必修之课。
维基百科说:冒泡排序对于数据量很大的集合排序来说效率会降低地很明显,反之其他算法比如插入排序对于同场景则更高效。 但作为编程语言初学者,掌握算法的概念和逻辑,冒泡排序足矣。
时间复杂度:冒泡排序的时间复杂度最坏情况是O(n²),平均时间复杂度是O(n²),最优的时间复杂度是O(n)。
冒泡排序名词由来:泡泡在浮出水面之前会被挤压显得很小,因此泡泡可以想象成集合中的元素。元素会经由比较与交换最终按照排序规则,如果是升序,则比值大的元素会排在最右端,反之会排在最左端。那泡泡呢,会在水里互相排挤,大的泡泡浮力大则最先浮出水面,以此类推。泡泡互相挤压并因浮力大小交错位置的行为可以理解为元素的比较与交换,泡泡全部浮出水面可以理解为算法排序结束。
📑 算法分析
例图
冒泡排序就是将上图的列表按照从大到小 (Ascending) 或 从小到大 (Descending) 顺序进行排序的一个过程。其中每一对 Array[i]、Array[i + 1],且 i < n - 1,作为一个比较单元,最终根据升降序规则进行元素交换。比如上图的 4 与 3、3 与 5 等等,都属于一个比较单元。
由于每一个列表中,都存在一个最大值或最小值,根据式子
可以推出,无论 A、B 或 C 处于什么顺序,A 和 Z 作为边界总是会最先被推到列表一端。那么确认最大值的时间复杂度,最坏为 O(n)。当 Array[0] = Max 时,即可证明。那么次于最大值的元素
Y,被排序后应该处于 Array[n - 2] 的位置,而相应的,X 应处于 Array[n - 3] 的位置。由于每一轮总是能确定 Array[n - i] 的位置,因此作为 j 的边界可以控制在 n - i 以内。
上图可以体会一下,第一轮排序后
3,4,5,5,2,4,2,6,8
即便 6 不在 n - 2 的位置,而在其它更靠前的位置,那么你可以想象从 4 向右推,最终也还是把 6 推向 n - 2 的位置,而 8 大于它,因此不会交换,结束本轮排序。
🖊 代码实现
for 循环版本
public void sort(int[] nums) {
int N = nums.length;
int tmp = 0;
for (int i = 0; i < N; i++) {
// N - 1 是为 j + 1 预留空间避免数组越界
// N - i 就是上面讲的控制 j 的边界
for(int j = 0; j < N - i - 1; j++) {
if(nums[j] > nums[j + 1]) {
// 元素交换
tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
while 循环版本
public static <T> void bubbleSort(T[] t, Comparator com, int right) {
// no need to sort if array's length equals to 1 or 0
if (right == 1 || right == 0)
return;
// both while and nested for looping are alright, it depends on your work requirement
while (true) {
// the var index represents the outer looping
int index = 0, edgeIndex = 0;
T temp;
for (int i = 1; i < right; i++) {
if (com.compare(t[i], t[index]) < 0) {
// swapping
temp = t[i];
t[i] = t[index];
t[index] = temp;
edgeIndex = i;
}
index++;
}
right = edgeIndex;
System.out.println(Arrays.toString(t));
if (right == 0) {
break;
}
}
}