排序算法指南:冒泡排序

前言:

        冒泡排序(Bubble Sort)是一种简单直观的排序算法。其核心原理是通过相邻元素的反复比较和交换,使较大元素逐渐"上浮"到序列末端,较小元素自然"下沉"到前端,最终实现整个序列的有序排列。

        冒泡排序作为我们接触的第一个排序算法,尽管实际应用中因其效率较低而较少采用,但它为理解排序算法的核心思想奠定了重要基础,对于任何排序算法,我们都可以采用从局部到整体的思维,先排好一趟元素,再进行全部元素的排序。

        

一、冒泡思想的介绍

        

为什么叫“冒泡”排序?

        

 想象一下你有一瓶雪碧。气泡(大的元素)会不断地往上冒,直到浮到水面(数组末尾)。

        

核心操作:两两元素进行比较,相邻之间交换

        

效果:每一轮循环结束,当前最大的那个数,就会像气泡一样,“漂”到了数组的最右边。

        

二、冒泡排序的工作原理

        

以排列升序元素为例,工作原理如下:

初始化: 从数组的第一个元素开始,逐一比较相邻的元素。

        

比较和交换: 如果当前元素比下一个元素大,则交换这两个元素的位置。

        

冒泡过程: 每一轮遍历后,未排序部分的最大元素会被移到该部分的末尾(类似于气泡浮起的过程)。

        

重复遍历: 继续对未排序部分进行相同的操作,每次遍历都会减少一个已排序的元素。

        

如下动图所示:

        

        

三、冒泡排序的实现

        

        以上述数组为例,假设有一个待排列的数组为:[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]。

第一趟遍历:

        

比较 3 和 44,3 < 44,不交换。

        

比较 44 和 38,44 > 38,交换:[3, 38, 44, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]

        

比较 44 和 5,44 > 5,交换:[3, 38, 5, 44, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]

        

比较 44 和 47,44 < 47,不交换。

        

比较 47 和 15,47 > 15,交换:[3, 38, 5, 44, 15, 47, 36, 26, 27, 2, 46, 4, 19, 50, 48]

        

比较 47 和 36,47 > 36,交换:[3, 38, 5, 44, 15, 36, 47, 26, 27, 2, 46, 4, 19, 50, 48]

        

比较 47 和 26,47 > 26,交换:[3, 38, 5, 44, 15, 36, 26, 47, 27, 2, 46, 4, 19, 50, 48]

        

比较 47 和 27,47 > 27,交换:[3, 38, 5, 44, 15, 36, 26, 27, 47, 2, 46, 4, 19, 50, 48]

        

比较 47 和 2,47 > 2,交换:[3, 38, 5, 44, 15, 36, 26, 27, 2, 47, 46, 4, 19, 50, 48]

        

比较 47 和 46,47 > 46,交换:[3, 38, 5, 44, 15, 36, 26, 27, 2, 46, 47, 4, 19, 50, 48]

        

比较 47 和 4,47 > 4,交换:[3, 38, 5, 44, 15, 36, 26, 27, 2, 46, 4, 47, 19, 50, 48]

        

比较 47 和 19,47 > 19,交换:[3, 38, 5, 44, 15, 36, 26, 27, 2, 46, 4, 19, 47, 50, 48]

        

比较 47 和 50,47 < 50,不交换。

        

比较 50 和 48,50 > 48,交换:[3, 38, 5, 44, 15, 36, 26, 27, 2, 46, 4, 19, 47, 48, 50]

        

此时,最大的元素 50 已经“冒泡”到数组的末端。

        

由一趟排序我们可以得到如下代码:        

        

	//排序一趟
    //j控制的是每趟需要排序元素的比较次数
	for (int j = 0; j < n - 1; j++)
	{
		if (a[j] > a[j + 1])
		{
			Swap(&a[j], &a[j + 1]);
		}
	}

        

对于 冒泡排序 来说,通常来说,经过 n-1 趟排序就足够了,因为在第 n-1 趟结束后,最后一个元素已经排好序,不再需要再参与排序。

        

解释:

        

第一轮遍历:比较相邻元素,最大值会“冒泡”到数组的末尾。

        

第二轮遍历:比较相邻元素,第二大的值会“冒泡”到倒数第二的位置。

        

以此类推...

        

所以,经过 n-1 趟之后,最后一个元素就已经是排序好的最大元素,不再需要参与接下来的排序。

        

故而对n个元素进行排序的代码如下,只需要再添加一层循环控制排序的趟数:

        

void BubbleSort(int* a, int n)
{
	//i控制的是排序的趟数
	for (int i = 0; i < n - 1; i++)
	{
		//排序一趟
		//j控制的是每趟需要排序元素的比较次数
        //每排一趟,需要排序需要排序的元素少一个
		for (int j = 0; j < n - 1- i; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
			}
		}

	}

}

        

四、冒泡排序的优化

        

对于已经有序数组,冒泡排序的优化

        

在标准冒泡排序的实现中,每一趟排序都会遍历整个数组,将相邻的元素进行比较和交换。

        

然而,对于已经有序的数组,比较操作会发现没有元素需要交换,这意味着每一趟遍历并不会发生交换。

        

所以我们可以通过添加标记,判断是否一趟排序有元素交换,如果没有则说明已经是有序数组,如果有则说明还要进行排序。

        

void BubbleSort(int* a, int n)
{
	
	//i控制的是排序的趟数
	for (int i = 0; i < n - 1; i++)
	{
        //定义一个标记,默认为0
		int flag = 0;
		//排序一趟
		//j控制的是每趟需要排序元素的比较次数
        //每排一趟,需要排序需要排序的元素少一个
		for (int j = 0; j < n - 1- i; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
                //出现交换,标记变为1
				flag = 1;
			}
		}
        
		if (!flag) break;
	}

}

        

五、冒泡排序时空复杂度的分析

1.时间复杂度分析

1.1最坏情况:O(n²)

        

在最坏的情况下(例如,输入数组是完全逆序的),冒泡排序会进行 n-1 趟排序

        

每一趟都需要进行 n-i-1 次相邻元素的比较和交换(i 是当前的遍历轮数)。

        

第 1 轮遍历:需要比较和交换 n-1 次

        

第 2 轮遍历:需要比较和交换 n-2 次

        

...

        

第 n-1 轮遍历:需要比较和交换 1 次

        

因此,总的比较次数为(等差数列求和):

        

(n−1)+(n−2)+⋯+2+1=(1 + n - 1 ) * (n - 1) / 2

        

最坏情况时间复杂度:O(n²)

        

1.2 最好情况:O(n)

        

在最好情况下,即数组已经有序时,冒泡排序依然会遍历一遍整个数组,但没有发生任何交换。

        

在优化版本的冒泡排序中,如果某一趟遍历没有发生交换,算法会提前终止。

        

这时,算法只会进行一次遍历,比较每一对相邻元素,但没有发生交换。由于没有交换,排序会在第一次遍历结束后提前终止。

        

最好情况时间复杂度:O(n)

        

1.3 平均情况:O(n²)

        

对于一个随机排列的数组,每一轮遍历可能会发生一些交换。

        

平均情况下,算法仍然需要进行大约 n²/2 次比较,因此时间复杂度为 O(n²)。

        

平均情况时间复杂度:O(n²)

        

2.空间复杂度

        

冒泡排序是 原地排序 算法,它只在原数组上进行操作,且只使用了常量级的额外空间,所以空间复杂度为O(1)。

        

3.总结

        
时间复杂度:

最坏情况:O(n²)

        

最好情况(优化版):O(n)

        

平均情况:O(n²)

        

空间复杂度:

因为冒泡排序是原地排序,所以空间复杂度为O(1)。

                

        

既然看到这里了,不妨关注+点赞+收藏,感谢大家,若有问题请指正。

                                        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值