冒泡排序超详解——C语言

冒泡排序

在讲解之前,我们先来搞清楚,冒泡排序究竟是怎样的。什么叫冒泡排序呢?冒泡排序的核心思想:两两相邻的元素比较,不满足顺序就交换,满足顺序就找下一对。
举个例子:这里给出一组数据
在这里插入图片描述
我们的目标是:给这组数据完成升序排序。小的在前面,大的在后面。我们来一对一对的比较。首先等待排序的一对是9和8。我们看到,9和8现在不满足升序的要求。因此,我们把它们交换位置。交换之后就变成了在这里插入图片描述

搞定这一对元素之后,比较9和7。因为不满足升序,所以给他俩也要交换位置。此时就变成了在这里插入图片描述
以此类推,最后我们会得到在这里插入图片描述
我们发现,比较下来,最大的数字9到了最后。我们把这一整个比较过程叫做一趟冒泡排序。一趟冒泡排序下来,我们搞定了一个元素:9。不管9刚开始在任何位置,经过一趟冒泡排序,它肯定会在最末尾。
搞定了9,剩下的是
在这里插入图片描述
我们再用相同的方法,对它们进行比较。首先是8和7。我们对他俩进行比较,就得到了在这里插入图片描述
然后再对他们相邻的元素进行比较也就是8和6进行比较,我们可以得到在这里插入图片描述
以此类推,经过多次交换,我们可以得到在这里插入图片描述
此时我们可以发现,在876543210这些数中,经过反复的比较和交换,8也来到了最后。我们可以得出:一趟冒泡排序可以解决一个元素的排序。假如我们给出个元素,那要进行多少趟排序呢?答案是9。因为一趟解决一个,一趟解决一个,九趟之后,最小的肯定已经在最前面去了,因此这个数字不需要再比较。当有n个元素,那就需要有(n - 1)趟。我们给每个元素给个下标。众所周知,数组的下标是从0开始的。因此,第一次比较其实就是下标为0的元素和下标为1的元素进行比较在这里插入图片描述
第二次就是下标为1的元素和下标为2的元素进行比较在这里插入图片描述

如果我们有个下标j,那就等于是把下标为jj+1的元素进行比较。我们再观察一下,第趟排序,10个元素,我们比较了9对;第趟排序,由于已经搞定了一个元素,因此只剩下9个待排序的元素,我们对它们进行了8对比较。
经过分析,我们可以来敲代码啦~以下就是经过上述分析得到的冒泡排序代码。

#include <stdio.h>
void input(int* arr, int sz)
{
	for (int i = 0;i < sz; i++)
	{
		scanf("%d", arr + i);
	}
}
void bubble_sort(int* arr, int sz)
{
	//确定趟数
	int i = 0;
	for (i = 0;i < sz - 1;i++)
	{
		//每一趟内部两个相邻的元素比较
		int j = 0;
		/*将arr[j] arr[j + 1]*/
		for (j = 0;j < sz - 1 - i;j++)
		{
			if (arr[j] > arr[j + 1])
			{
				//交换
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0;i < sz;i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 0 };
	//输入一些值
	int sz = sizeof(arr) / sizeof(arr[0]);//sz 表示输入的元素个数
	input(arr, sz);
	//排序-写一个函数完成数组的升序排序
	bubble_sort(arr, sz);
	print(arr,sz);
	return 0;
}

这个程序实现了输入一组整数,然后使用冒泡排序对这些整数进行升序排序,最后将排序后的结果输出。让我们来分析一下这个代码:

  1. input函数:
  • input 函数通过循环从标准输入中读取整数,并将它们存储到传入的数组 arr 中。
  • 这个函数使用了指针算术 (arr + i) 来访问数组的每个元素,并通过 scanf 函数读取输入的整数值。
  1. bubble_sort 函数:
  • bubble_sort 函数实现了冒泡排序算法。
  • 冒泡排序的基本思路是,依次比较相邻的两个元素,如果它们的顺序不对则交换,这样每一趟可以将未排序部分的最大元素冒泡到正确的位置。
  • 外层循环控制趟数,内层循环对每一趟进行相邻元素的比较和交换操作。
  1. print 函数:
  • 使用循环遍历数组,并使用 printf 函数逐个输出数组元素的值
  1. main 函数:
  • 使用 sizeof(arr) / sizeof(arr[0]) 计算出数组的大小,存储在变量 sz 中,用于确定输入的元素个数。
  • 调用 input 函数从标准输入中读取输入的整数,然后调用 bubble_sort 函数对数组进行排序。
  • 最后调用 print 函数输出排序后的数组。

我们来做一组测试:输入一组数据,进行排序
在这里插入图片描述
可见,这个程序顺利完成数据的升序排序。

然而,这种算法其实在某种情况下,会特别浪费时间。比如说:输入的数据是1 2 3 4 5 6 7 8 9 10,要求我们排成升序。我们发现,此时输入的数据已经是升序,但是程序还是会将他们挨个挨个比较进行排序。因此我们可以对程序进行一下优化:在这里插入图片描述

我们在每一趟开始的时候,都进行一下判断,假设已经满足顺序。在这里插入图片描述
优化的目标是在列表已经有序的情况下提前结束排序过程,而不是继续执行多余的遍历。这就是 flag 变量的作用所在:

  1. 初始化:在每一趟排序开始时,假设列表已经有序,将 flag 初始化为 1

  2. 比较与交换:在每次比较过程中,如果发现有任何一对相邻的元素需要交换,则表明列表仍然不完全有序,将 flag 设置为 0。

  3. 在内层循环结束后,通过检查 flag 的值来确定是否有元素交换过。如果 flag 保持为 1,说明整个列表已经有序,此时可以提前结束外层循环,无需继续进行后续的排序操作。

通过这种方式,当输入的数据已经基本有序时,可以避免不必要的比较和交换操作,从而提高冒泡排序的效率。
下面是优化过后的代码:

#include <stdio.h>
void input(int* arr, int sz)
{
	for (int i = 0;i < sz; i++)
	{
		scanf("%d", arr + i);
	}
}
void bubble_sort(int* arr, int sz)
{
	//确定趟数
	int i = 0;
	for (i = 0;i < sz - 1;i++)
	{
		int flag = 1;//假设已经满足顺序
		//每一趟内部两个相邻的元素比较
		int j = 0;
		/*将arr[j] arr[j + 1]*/
		for (j = 0;j < sz - 1 - i;j++)
		{
			if (arr[j] > arr[j + 1])
			{
				flag = 0;//还不是有序
				//交换
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0;i < sz;i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 0 };
	//输入一些值
	int sz = sizeof(arr) / sizeof(arr[0]);//sz 表示输入的元素个数
	input(arr, sz);
	//排序-写一个函数完成数组的升序排序
	bubble_sort(arr, sz);
	print(arr,sz);
	return 0;
}

做个测试:我们来看看输入不同的数据,各自比较了多少对数据。在这里插入图片描述
然后在主函数中打印count:在这里插入图片描述
代码走起来!在这里插入图片描述
这里我们输入了一对已经有序的数据。可以看到,程序只进行了9对比较,也就是一趟冒泡排序。
但我们想一下,如果没有flag,会进行多少次比较:在这里插入图片描述

我们可以看到,如果没有flag判断,程序对这组已经有序的数据依旧进行了45次比较。因为第趟冒泡排序我们要进行9对判断;第趟要进行8对;第趟要进行7对……以此类推进行了45对比较,比较浪费时间。

OK,以上就是冒泡排序的详细讲解。希望这篇文章能给你带来些许收获。葛大侠是小白一个,有什么写的不对的地方,欢迎在评论区点评留言。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值