冒泡排序详解

冒泡排序详解

冯唐正
电子工程与光电技术学院 南京理工大学

冒泡排序概述

冒泡排序(英语:Bubble Sort)又称为泡式排序,是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序图解

算法复杂度

对于问题规模为n的数组进行冒泡排序,需要进行O(n^2)的比较次数。

算法原理

冒泡排序演算法的运作如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

原理概念图

伪代码

我们可以写出如下形式的升序冒泡的伪代码

函数 泡沫排序 输入 一个阵列名称为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次。

参考文献

E-mail

如有任何疑问,Email me at lorenzo.feng@njust.edu.cn

  • 14
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正崽不emo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值