冒泡排序的基本概述
冒泡排序(Bubble Sort)是一种基础的排序算法,它通过重复遍历待排序的数列,比较每对相邻元素的大小,并在必要时交换它们的位置。冒泡排序的步骤可以概括如下:
- 开始排序:从数列的第一个元素开始,对它和下一个元素进行比较。
- 比较与交换:如果第一个元素比第二个元素大,则交换它们的位置。这样,经过第一次比较后,数列的最后一个元素就是最大的元素。
- 重复遍历:继续对数列的剩余部分进行同样的操作,但每次遍历都可以忽略已经确定位置的最大元素。
- 一轮排序:完成一轮遍历后,当前未排序部分的最大元素会“冒泡”到它应在的位置。
- 多轮遍历:重复上述过程,直到整个数列都被排序。
- 算法结束:当进行一轮遍历没有发生任何交换时,说明数列已经完全排序。
冒泡排序的特点包括:
- 稳定性:相等元素的相对顺序在排序后保持不变。
- 时间复杂度:在最坏情况下(即初始数列为逆序),时间复杂度为O(n^2),在最好情况下(即初始数列已经排序),时间复杂度为O(n)。
- 空间复杂度:冒泡排序是原地排序,空间复杂度为O(1)。
需要注意的是,冒泡排序由于其简单性,通常用于教育目的或对小规模数据集进行排序。然而,对于大规模数据集,由于其较低的效率,通常会选择更高效的排序算法,如快速排序、归并排序或堆排序。
时间复杂度和空间复杂度
时间复杂度和空间复杂度是评估算法性能的两个重要指标,它们分别描述了算法在执行过程中所需的时间和存储空间。不同的算法具有不同的时间和空间复杂度,选择适当的算法可以提高程序的性能和资源利用率。了解算法的复杂度有助于识别性能瓶颈,进行优化。另外,随着数据量的增长,算法的复杂度直接影响程序的可扩展性。
时间复杂度
时间复杂度是衡量算法执行时间与输入规模(通常是数据量)之间关系的指标。它通常用大O表示法来描述。例如,O(1), O(n), O(n^2), O(log^n) 等,这些符号提供了算法执行时间随输入规模增长的上界估计。
-
最好情况(Best Case,算法在最理想情况下的时间复杂度,例如输入数据已经部分排序):当输入的数列已经是完全排序的状态时,冒泡排序只需进行一轮遍历即可发现没有需要交换的元素,因此可以在(O(n))的时间复杂度内完成排序。这里的(n)是数列中元素的数量。
-
最坏情况(Worst Case,算法在最不理想情况下的时间复杂度,例如输入数据完全逆序):当输入的数列是完全逆序时,冒泡排序需要进行(n-1)轮遍历,每轮遍历需要进行(n-k)次元素比较和可能的交换,其中(k)是当前已经排好序的元素数量。总的时间复杂度是(O(n^2)),因为每轮遍历的比较次数依次减少,形成了一个等差数列求和的情况。
-
平均情况(Average Case,算法在所有可能的输入情况下的平均时间复杂度):在平均情况下,冒泡排序的时间复杂度也是(O(n^2))。这是因为在随机分布的数列中,每次元素交换的可能性是随机的,但平均来看,算法仍然需要进行接近(n^2)次的比较和交换。
空间复杂度
空间复杂度是衡量算法执行过程中所需的存储空间量与输入规模之间的关系。它同样使用大O表示法来描述:
-
原地排序(In-Place Sorting,算法在不增加额外存储空间的情况下对输入数据进行排序,例如冒泡排序):冒泡排序不需要额外的存储空间来创建另一个数列,它直接在原始数列上进行操作。这意味着它使用的空间只与排序的数列的大小无关,即(O(1))。
-
非原地排序(Non-In-Place Sorting,算法需要额外的存储空间来辅助排序,例如归并排序):非原地排序算法在排序过程中可能需要分配与输入规模成正比的额外存储空间。例如,归并排序算法在合并步骤中就需要额外的存储空间来暂存合并后的元素。
-
空间复杂度分析:尽管冒泡排序在执行过程中可能会使用到一些辅助变量(如用于元素交换的临时变量),但这些变量的数量是固定的,不随输入规模(n)的变化而变化。因此,冒泡排序的空间复杂度是常数级别的,通常表示为(O(1))。
冒泡排序的代码实现
使用JavaScript和Python实现冒泡排序。
JavaScript冒泡排序实现
function bubbleSort(arr) {
let n = arr.length;
// 外层循环控制排序轮数,n-1轮
for (let i = 0; i < n - 1; i++) {
// 前提设定没有发生交换
let swapped = false;
// 内层循环进行每轮的两两比较和交换
for (let j = 0; j < n - i - 1; j++) {
// 如果当前元素大于下一个元素,交换它们
if (arr[j] > arr[j + 1]) {
// 交换元素
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
// 标记发生了交换
swapped = true;
}
}
// 如果在这一轮排序中没有发生交换,提前结束
if (!swapped) {
break;
}
}
return arr;
}
// 使用示例
let array = [64, 34, 25, 12, 22, 11, 90];
console.log("Sorted array:", bubbleSort(array));
Python冒泡排序实现
def bubble_sort(arr):
n = len(arr)
# 外层循环控制排序轮数,n-1轮
for i in range(n - 1):
# 前提设定没有发生交换
swapped = False
# 内层循环进行每轮的两两比较和交换
for j in range(0, n - i - 1):
# 如果当前元素大于下一个元素,交换它们
if arr[j] > arr[j + 1]:
# 交换元素
arr[j], arr[j + 1] = arr[j + 1], arr[j]
# 标记发生了交换
swapped = True
# 如果在这一轮排序中没有发生交换,提前结束
if not swapped:
break
return arr
# 使用示例
array = [64, 34, 25, 12, 22, 11, 90]
print("Sorted array:", bubble_sort(array))
时间复杂度分析:
- 最坏情况下,即数组完全逆序,外层循环将执行(n-1)次,内层循环在第一轮执行(n-1)次,在第二轮执行(n-2)次,依此类推。总的比较次数接近(n(n-1)/2),是(O(n^2))的。
- 最好情况下,数组已经是排序状态,经过一轮遍历后,如果发现没有发生交换,
swapped标志将保持false,循环将提前结束,时间复杂度为(O(n))。
空间复杂度分析:
- 冒泡排序是原地排序算法,除了输入数组外,只使用了
temp和swapped两个额外变量。无论数组大小如何,这些变量的数量是固定的。 - 因此,空间复杂度是(O(1)),即常数级别的空间复杂度。
引用说明: 本文引用自coderwhy.(2023).[TypeScript实现十大排序算法(一) - 冒泡排序].juejin.cn/post/720175…
致谢: 本文在撰写过程中参考了coderwhy的见解,特此致谢。
版权声明: 如果使用了版权材料,请确保遵守版权法,并在文章中声明您已获得使用许可或遵循了合理使用原则。