【七大排序】最基础的排序——冒泡排序

本文详细介绍了冒泡排序的基本思想、C语言代码实现、优化方法以及时间复杂度和空间复杂度分析,突出了冒泡排序作为基础排序算法的价值和局限性。
摘要由CSDN通过智能技术生成


前言

  常见的排序算法有七种。

  本篇文章将会详细介绍冒泡排序的实现。

一、冒泡排序的思想

  以升序为例,冒泡排序本质上就是不断两两比较,找出较大者,让其移动到右侧,然后再将其与下一个值两两比较,重复以上步骤,让它像一个泡泡一样,移动到末尾。这样就能够排好最大值的位置,然后从头再来一遍,找出第二大的,也让它像个泡泡一样,移动到倒数第二个位置。每次遍历都能够排好一个数的位置,最终就能够排好整个顺序。
在这里插入图片描述

二、代码实现

1.完整代码

#include<stdio.h>

// 冒泡排序--升序
void BubbleSort(int* arr, int size)
{
    // 最外层循环,用来控制循环次数,每次排好一个数据
	for (int i = 0; i < size - 1; ++i)
	{
	    // 内层循环,用于单次遍历数组,每在末尾排好一个数据则不需要再次遍历到末尾
		for (int j = 0; j < size - 1 - i; ++j)
		{
		    // 如果左边的数据比右边的数据大
			if (arr[j] > arr[j + 1])
			{
			    // 使用异或符号来 交换 这两个值
				arr[j] = arr[j] ^ arr[j + 1];
				arr[j + 1] = arr[j] ^ arr[j + 1];
				arr[j] = arr[j] ^ arr[j + 1];
			}
			// 此时保证右侧数据比左侧大
		}
		// 保证这一次循环结束后使当前最大值在数组末尾
	}
	// 保证循环 数组长度-1 次后排好所有的数据顺序
}

// 主函数
int main()
{
    // 手搓的一个无序数组
	int arr[] = { 684,913,621,333,746,249,159,428,764,349,587,616,235,844 };
	// 数组长度
	int len = sizeof(arr) / sizeof(arr[0]);
	// 调用冒泡排序--升序
	BubbleSort(arr, len);
	// 循环打印数组内容
	for (int i = 0; i < len; ++i)
	{
		printf("%d ", arr[i]);
	}
	// 换行
	printf("\n");
	return 0;
}

2.结果演示

在这里插入图片描述

3.要点分析

  冒泡排序非常简单,只需要嵌套两次循环即可解决。需要注意的是,每遍历一次数组,就能将无序部分的最大值移动到数组末尾,我们要 排序/交换 的部分是无序部分,所以每遍历一次数组,数组的有序部分就会增加,无序部分就会减少,单次遍历的条件就要根据次数来改变。

三、优化

1.优化分析

  上面的代码不管什么情况都需要遍历 数组长度-1 次数组,如果数组是较为有序的数组,只需要短短几次就能够排好序的那种数组,上面的代码就会在即使数组已经有序的情况下,冒泡排序仍然要去遍历那么多次,比较麻烦,所以我们在每次的单次遍历后判断一下数组是否有序,有序的话直接终止循环即可。

2.优化代码

#include<stdio.h>
#include<stdbool.h>

// 冒泡排序--升序
void BubbleSort(int* arr, int size)
{
    // 最外层循环,用来控制循环次数,每次排好一个数据
	for (int i = 0; i < size - 1; ++i)
	{
	    // 用布尔值假设数组有序
	    bool k = true;
	    // 内层循环,用于单次遍历数组,每在末尾排好一个数据则不需要再次遍历到末尾
		for (int j = 0; j < size - 1 - i; ++j)
		{
		    // 如果左边的数据比右边的数据大
			if (arr[j] > arr[j + 1])
			{
			    // 使用异或符号来 交换 这两个值
				arr[j] = arr[j] ^ arr[j + 1];
				arr[j + 1] = arr[j] ^ arr[j + 1];
				arr[j] = arr[j] ^ arr[j + 1];
				// 只要在单次遍历中发生了交换,则说明还不能判断数组真的有序,k改为假有序
				k = false;
			}
			// 此时保证右侧数据比左侧大
			// 如果k值为真,说明本次遍历中没有发生交换,说明数组真的有序了
			if (k == true)
				// 退出循环
				break;
		}
		// 保证这一次循环结束后使当前最大值在数组末尾
	}
	// 保证循环 数组长度-1 次后排好所有的数据顺序
}

// 主函数
int main()
{
    // 手搓的一个无序数组
	int arr[] = { 684,913,621,333,746,249,159,428,764,349,587,616,235,844 };
	// 数组长度
	int len = sizeof(arr) / sizeof(arr[0]);
	// 调用冒泡排序--升序
	BubbleSort(arr, len);
	// 循环打印数组内容
	for (int i = 0; i < len; ++i)
	{
		printf("%d ", arr[i]);
	}
	// 换行
	printf("\n");
	return 0;
}

3.要点分析

  这个优化只需要记录单次遍历后数组有没有发生交换,如果发生了交换,我们就猜不到数组是否有序,需要以无序来看待,进行下次遍历。如果没有发生交换,说明数组任意两两元素都满足条件,则数组已经有序,即可直接退出排序。

四、复杂度分析

1.时间复杂度

  在最坏情况下,假设数组长度为 n ,那么我们排好一个数组需要 n - 1 次遍历。而每一次排序都会使无序部分减少 1 ,所以第一次遍历需要判断 n - 1 次,第二次遍历需要判断 n - 2 次。。。。最后一次,第 n - 1 次遍历需要判断 1 次,通过数学计算,可算出需要判断 (n(n - 1)) / 2 次。所以时间复杂度为 O(n2)

2.空间复杂度

  冒泡排序并没有额外开辟数组空间。所以空间复杂度为 O(1)


总结

  冒泡排序属于非常简单的排序,也是大多数人第一次接触到的排序,虽然排序的效率不高,但是对了解排序思想还是有帮助的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值