冒泡排序详解
冒泡排序概述
冒泡排序(英语:Bubble Sort)又称为泡式排序,是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
算法复杂度
对于问题规模为n的数组进行冒泡排序,需要进行O(n^2)的比较次数。
算法原理
冒泡排序演算法的运作如下:
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
伪代码
我们可以写出如下形式的升序冒泡的伪代码
函数 泡沫排序 输入 一个阵列名称为array 其长度为length
i 从 0 到 (length - 1)
j 从 0 到 (length - 2 - i)
如果 array[j] > array[j + 1]
交换 array[j] 和 array[j + 1] 的值
如果结束
j回圈结束
i回圈结束
函数结束
C++代码
/*
* @Description : Sort an array of T-type.
* @Parameters : arr:array to sort.
* @Parameters : len:the length of the part to sort of arr.
* @Parameters : ascending:true for ascending while false for descending.
* @Usage Sample : bubbleSort(arr,sizeof(arr)/sizeof(*arr),false);
* @Author : Tangzheng Feng
* @Date : Nov. 12th. 2023
*/
template<typename T>
void bubbleSort(T arr[],int len, bool ascending = true) {
for (int i = 0; i < len; i++) {
for (int j = 0; j < len- i - 1; j++) {
if ((arr[j + 1] - arr[j]) * (ascending * 2 - 1) < 0) {
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
Python代码
def bubble_sort(arr, ascending: bool = True) -> None:
'''
@description : bubble-sort an array.
@parameters : arr: array to sort.
@parameters : ascending: True for ascending while False for descending.
@author : Tangzheng Feng
@date : Nov. 12th. 2023
'''
for i in range(len(arr)):
for j in range(len(arr) - i - 1):
if (arr[j + 1] - arr[j]) * (ascending * 2 - 1) < 0:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
用法示例
升序
主程序代码:
cpp:
int main()
{
int arr[] = { 11,56,86,2,-9,56 };
bubbleSort(arr,sizeof(arr) / sizeof(*arr));
for (auto i : arr) {
std::cout << i << '\t';
}
return 0;
}
py:
if __name__ == "__main__":
arr = [11,56,86,2,-9,56]
bubble_sort(arr)
for i in arr:
print(i,end='\t')
运行结果:
降序
主程序代码:
cpp:
int main()
{
double arr[] = { 0.5,0.7,1.6,-1.7,0.86,-0.8 };
bubbleSort(arr,sizeof(arr) / sizeof(*arr),false);
for (auto i : arr) {
std::cout << i << '\t';
}
return 0;
}
py:
if __name__ == "__main__":
arr = [0.5,0.7,1.6,-1.7,0.86,-0.8]
bubble_sort(arr,False)
for i in arr:
print(i,end='\t')
运行结果:
代码分析
升降序
我们通过内层循环的if语句来控制我们的排序方向,即当if的条件为true时,交换arr[j]和arr[j+1]的值。换句话说,if语句内arr[j]和arr[j+1]的大小和我们排序方向呈相反的方向。
在我的代码中我做了一些数学上的工作来使得它们统一成如下形式:
if ((arr[j + 1] - arr[j]) * (ascending * 2 - 1) < 0)
冒泡方向
在上述代码中我们默认的冒泡方向是从0到len-1,也就是说,在每一次内层循环结束的时候,arr[len-i-1]到arr[len-1]的值是有序的。
我们也可以改变冒泡的方向,即从len-1向0冒泡,使得每一次内层循环结束的时候,arr[0]到arr[i]是有序的。一个可能的版本如下:
cpp:
template<typename T>
void bubbleSort(T arr[], int len, bool ascending = true) {
for (int i = 0; i < len; i++) {
for (int j = len - 1; j > i; j--)
{
if ((arr[j - 1] - arr[j]) * (2 * ascending - 1) > 0)
{
T temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
}
py:
def bubble_sort(arr, ascending: bool = True) -> None:
for i in range(len(arr)):
for j in range(len(arr) - 1, i):
if (arr[j - 1] - arr[j]) * (ascending * 2 - 1) > 0:
arr[j], arr[j - 1] = arr[j - 1], arr[j]
排序范围
在上面的示例代码中我们对整个arr进行冒泡排序,当我们传入的len相较于sizeof(arr)/sizeof(*arr)较小的值的时候,我们排序的范围范围则变成从arr[0]到arr[len-1]。
取值分析
我们可以通过越界法判断一段冒泡算法的代码边界值的有效性。例如,请思考下述代码的合理性。
cpp:
template<typename T>
void bubbleSort(T arr[], int len, bool ascending = true) {
for (int i = 0; i < len; i++) {
for (int j = len - 1; j > i; j--)
{
if ((arr[j] - arr[j + 1]) * (2 * ascending - 1) > 0)
{
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
py:
def bubble_sort(arr, ascending: bool = True) -> None:
for i in range(len(arr)):
for j in range(len(arr) - 1, i):
if (arr[j] - arr[j + 1]) * (ascending * 2 - 1) > 0:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
我们可以知道,i的取值是[0,len)(区间上的整数,j同理),j的取值是(i,len-1],则对于所有的j的集合,j的取值是(0,len-1],如此,这段代码即无法遍历到arr[0]的位置,也会发生arr[len]的越界错误(在j==len-1时,j+1为len)。因此,我们应该在内层循环中比较arr[j]与arr[j-1]。
交换次数
与比较次数不同,并不是每次循环都会进行交换,而统计交换次数的方法通常采用取逆序对法,例如,有一个数组a=[15,163,563,153,86,20],若要将其升序排序,则需要依次取降序数对,一共有(163,153)、(163,86)、(163,20)、(563,153)、(563,86)、(563,20)、(153,86)、(153,20)、(86,20)这9对逆序对,因此在冒泡排序中我们需要交换9次。
参考文献
- [1] Wikipedia
如有任何疑问,Email me at lorenzo.feng@njust.edu.cn