【C语言】冒泡排序详解

前言

排序,就是将一组数据按特定的规则调换位置,使这组数据具有某种顺序关系,一般就是递增或递减。

在接触C语言不久,我们就会遇到其中一种有名的排序算法——“冒泡排序”,不知道你是否已经掌握了,如果还没有的话,不妨听一下我的讲解版本,或许能有一点启发。

记住,理解冒泡排序非常重要的一点是,你要知道这个名字“冒泡”并不是随便乱取的,通过画图你可以看到它的过程真的就像在“冒泡”,可以抓住这个形象的过程去理解。 当然冒泡可以向左冒,也可以向右冒。

讲解

实现冒泡的逻辑:

冒泡排序指的是在排序时,每次比较数组中相邻的两个数组元素的值,(假设我们现在要从小到大排序)如果前面的数大于后面的数,就将较小的数调换到较大的数前面。

现在我们假设有一组数据:7,6,14,5,2,如果用冒泡法对其进行排序,排成从小到大,那么每轮排序的过程我们可以画图:

图中下划线代表当下正在两两比较的两个数据。可以看到两两比较的结果就是14作为当前数组中最大的数被一路调换位置,到了数组的末尾位置(因为14作为数组最大的数,其实已经到了该到的位置,下一轮两两比较就与14无关了,它就像一个冒出当前范围的泡泡) 

以上是一轮冒泡,现在我们再看一轮:

此时可以看到,7被一路换到了14的前面也就是它应该到的位置,14则无需再进行比较,下一轮的7也无需再参与两两比较了。 

可以看到每一轮冒泡我们就能让一个数去到它该去的位置,也可以总结出一轮冒泡的规律:

冒泡排序每一轮冒泡就是一次整个数组的两两比较,规则很简单,两两比较所以会有是从数组第一个元素开始的,而当遍历到了最后一个元素就不会再往后遍历了因为已经没有元素了,而此时我们的前是到倒数第二个元素,所以我们可以得出每次冒泡(也就是每轮整个数组的两两遍历)的代码应该是:

int j = 0;
for (j = 0; j < sz - 1 - i; j++)//一会解释
{
	if (arr[j] > arr[j + 1])//如果“前”比“后”大,就交换
	{
		int tmp;//创建一个中间变量,用于交换
		tmp = arr[j];
		arr[j] = arr[j + 1];
		arr[j + 1] = tmp;
	}
}

在上面我们不断提到“”这个字眼,因为我们冒几次泡是需要确定的,上面代码中的for语句执行一次只能冒出一个泡泡,也就是只能让当前最大的数去到它应该去的位置,所以为了让所有数都去到应该取的位置,我们就需要将这个for语句循环sz-1次。sz-1是什么?

int arr[] = { 7,6,14,5,2 };
int sz = sizeof(arr) / sizeof(arr[0]);//数组元素个数

sz是数组元素个数,那么sz-1就是我们应该冒泡的轮数,因为当sz-1个元素都去到了该去的位置,剩下的那个也已经无需再排了。

所以我们现在再写一个外循环来控制上面这个负责两两比较的for循环的执行次数:

int i = 0;
for (i = 0; i < sz - 1; i++)
{

	int j = 0;
	for (j = 0; j < sz - 1 - i; j++)
	{
		if (arr[j] > arr[j + 1])
		{
			int tmp;
			tmp = arr[j];
			arr[j] = arr[j + 1];
			arr[j + 1] = tmp;
		}
	}

}

那么,现在我来解释一下为什么内循环要写成for (j = 0; j < sz - 1 - i; j++),这个j < sz - 1 - i应该怎么理解呢?

我们上面说到,除了第一轮冒泡外,每一轮冒泡都有我们不去排的数,也就是先前已经排好了的数,上面第二轮冒泡我们不排的是14,第三轮冒泡我们不排的是14与7。这也说明我们每一轮冒泡需要两两比较的次数是不一样的,是变化的。而这个变化恰好就与我们的i变量有关,或者说恰好可以用变量i来表示:

如果没有-i,for (j = 0; j < sz - 1; j++)是什么效果?每一轮我们都会两两比较sz-1次,对于这个例子,5个元素,我们冒泡4轮,每一轮两两比较4次肯定是没必要的。

那for (j = 0; j < sz - 1 - i ; j++),带上-i后是什么效果? 第一轮冒泡,此时i为0,j<sz-1,也就是这一轮我们要两两比较4次,没错; 第二轮冒泡,此时i为1,j<sz-2,也就是两两比较3次,没错……最后一轮,此时i为sz-2也就是3,j<sz-4,也就是两两比较1次,没错。

所以可以看出,写成for (j = 0; j < sz - 1 - i ; j++)是非常正确的、符合预期的。

交换两个元素的值:

如果不理解交换两个元素的值的代码为什么这么写的读者,也可以再听一下解释:

int tmp;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;

可能会有人好奇为什么不能写成:

arr[j] = arr[j + 1];
arr[j + 1] = arr[j];

这是初学C语言的我们确实可能会犯下的错,注意代码的执行顺序是从上到下,而arr[j] = arr[j + 1];的'='可不是等号,是赋值运算符所以当执行到arr[j + 1] = arr[j];的时候其实此时的arr[j]的值已经变为arr[j + 1]的值了,这相当于把arr[j + 1]的值再赋值给arr[j + 1],是不能达到我们预期的交换的效果的。

所以我们可以先把arr[j]的值赋给一个额外创建的变量tmp,将arr[j + 1] 的值赋给arr[j],要将arr[j]原本的值赋给arr[j + 1]时我们只用将tmp的值赋给arr[j + 1]就行了。

所以讲到这里,我们已经得出了冒泡排序的代码,我们可以将其封装成一个函数:

void bubble_sort(int* arr, int sz)//传过来的是数组名arr,是数组首元素地址,所以用整型指针接收arr
                    //因为传过来的是数组首元素地址,所以不知道数组大小,所以要把元素个数一起传过来
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

相应的,main函数里应该是这样的:

int main()
{
	int arr[] = { 7,6,14,5,2 };
	int sz = sizeof(arr) / sizeof(arr[0]);//数组元素个数
	bubble_sort(arr,sz);//调用冒泡排序函数
	print(arr, sz);//排序完打印看看效果
	return 0;
}

这时可以看到我还写了一个函数print()专门用于打印数组,这样可以让我们的main函数更整洁,也可以提高复用性,下次打印就不用再写一次了,这是封装函数的好处,我们要尽早习惯这种做法。

//写一个函数实现对数组的打印
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

完整代码参考

vs运行效果:

 

可以看到我们的这组数据就成功地从小到大排序了。 

小结

我们可以总结出一些东西来帮助我们理解冒泡排序:

n个数冒泡排序,就要进行n-1轮冒泡;(第一轮n-1次两两比较)

对于我们的双层循环,i控制的是轮数,j才是负责控制两两比较的;

每轮要进行的两两比较是越来越少的,恰好可以用 j < sz - 1 - i表示;

每一轮冒泡,可以理解为把当前要比较范围内的最值一路冒到了最边上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/641042.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024最新 Jenkins + Docker 实战教程(五)- 配置Gitee Webhooks实现自动构建部署

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

等保建设:打造MySQL数据库审计系统

1、建设目标 在等级保护三级->应用安全->安全审计中强制需要有审计平台(满足对操作系统、数据库、网络设备的审计&#xff0c;在条件不允许的情况下&#xff0c;至少要使用数据库审计) 数据库审计服务符合等级保护三级标准&#xff0c;帮助您满足合规性要求&#xff0c;…

什么是组态?什么是工业控制中的组态软件?

随着工业4.0和智能制造的发展&#xff0c;工控软件的应用越来越广泛&#xff0c;它们在提高生产效率、降低能耗和减少人力成本等方面发挥着越来越重要的作用。 什么是工控软件&#xff1f; 工控软件是指用于工业控制系统的软件&#xff0c;主要应用于各种生产过程控制、自动化…

Java中流的概念细分

按流的方向分类&#xff1a; 输入流&#xff1a;数据流向是数据源到程序&#xff08;以InputStream、Reader结尾的流&#xff09;。 输出流&#xff1a;数据流向是程序到目的地&#xff08;以OutputStream、Writer结尾的流&#xff09;。 按处理的数据单元分类&#xff1a; 字…

在winnas中使用docker desktop遇到的问题及解决方法记录

最近在尝试从群晖转向winnas&#xff0c;一些简单的服务依然计划使用docker来部署。群晖的docker简单易用且稳定&#xff0c;在win上使用docker desktop过程中遇到了不少问题&#xff0c;在此记录一下以供后来人参考。 一、安装docker desktop后启动时遇到无法启动docker引擎 …

构建数字未来:探索Web3在物联网中的新视角

引言 随着Web3时代的来临&#xff0c;物联网技术正迎来一场新的变革。在这个数字化时代&#xff0c;Web3所带来的技术创新将为物联网的发展开辟新的视角。本文将深入探讨Web3在物联网领域的应用&#xff0c;揭示其在构建数字未来中的重要性和影响。 Web3与物联网的融合 区块链…

运用HTML、CSS设计Web网页——“西式甜品网”图例及代码

目录 一、效果展示图 二、设计分析 1.整体效果分析 2.头部header模块效果分析 3.导航及banner模块效果分析 4.分类classify模块效果分析 5.产品展示show模块效果分析 6.版权banquan模块效果分析 三、HTML、CSS代码分模块展示 1. 头部header模块代码 2.导航及bann…

QQ个性网空间日志网站模板源码

QQ个性网空间日志网站模板源码自带后台登录设置&#xff0c;适用于博客、文章、资讯、其他类网站内容使用。模板自带eyoucms内核&#xff0c;原创设计、手工书写DIVCSS&#xff0c;完美兼容IE7、Firefox、Chrome、360浏览器等;主流浏览器;结构容易优化;多终端均可正常预览。由于…

保安维稳,四信以科技构筑高速公路安全智慧防线

近日&#xff0c;广东梅大高速发生严重塌方事故&#xff0c;造成了严重的人员伤亡和财产损失。这一事件在公众心中敲响了安全的警钟&#xff0c;再次引起了公众对于交通设施运营安全性的重点关注。 国务院安委会办公室和国家防灾减灾救灾委员会办公室等主管机构先后印发紧急通知…

FuTalk设计周刊-Vol.053

#AI漫谈 热点捕手 1.Midjourney推出新功能Room 用户可在聊天室中一起创作图像 Midjourney最近推出了一个有趣的新功能——Room&#xff0c;为用户提供了一个协作和社交平台&#xff0c;用户可以一起创建和分享图像&#xff0c;并参与实时聊天。Room促进了用户之间的互动和合作…

Mujava 工具的简单使用

首先下载openjava.jar和mujava.jar&#xff0c;以及自己手写一个mujava.config指向存放mujava的目录&#xff0c;并将这些文件放在mujava目录下。此时&#xff0c;基本的mujava环境就搭建好了。 分别创建src&#xff08;存放源码文件&#xff09;、classes&#xff08;存放源码…

excel poi的titleRows 和 headRows含义

titleRows 这个参数的意思是&#xff1a;excel标题占多少行&#xff0c;而不是第几行headRows 这个参数的意思是&#xff1a;excel表头占几行&#xff0c;而不是第几行&#xff08;多行的意思是合并的行数&#xff09; 比如有一个excel如下&#xff0c;1-2行是标题&#xff0c…

webstorm新建vue项目相关问题

前言 这个迭代后端需求偏少&#xff0c;前端code的键盘都起火星子了。来了4个外包支持&#xff0c;1个后端3个前端&#xff0c;还是不够用啊。刚好趁这个机会稍微学习下vue&#xff0c;其实之前环境也配置过了&#xff0c;所以这里就不分享环境配置了&#xff0c;主要分享下新建…

Orangepi Zero2 linux系统摄像头设备文件名固定

文章目录 1. 寻找设备规则2. 使用udev规则修改挂载设备文件名称 问题&#xff1a; 在多次插拔usb摄像头或者在使用中不小心碰到或松了会导致设备文件名称变化&#xff0c;如从/dev/video1和/dev/video2变为/dev/video2和/dev/video3, 所以每次发生变化后都要充型修改代码或者重…

安全访问python字典:避免空键错误的艺术

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、直接访问字典键的问题 三、使用get方法安全访问字典键 四、get方法的实际应…

C语言中的七种常用排序

今天&#xff0c;为大家整理了C语言中几种常用的排序&#xff0c;以及他们在实际中的运用&#xff08;有Bug请在下方评论&#xff09;&#xff1a; 一.桶排序 #include <stdio.h> int main() {int book[1001],i,j,t,n;for(i0;i<1000;i)book[i]0;scanf("%d"…

第八节 条件装配案例讲解

一、条件装配的作用是什么 条件装配是 Spring 框架中一个强大的特性&#xff0c;使得开发者能够创建更加灵活和可维护的应用程序。在 Spring Boot 中&#xff0c;这个特性被大量用于自动配置&#xff0c;极大地简化了基于 Spring 的应用开发。 二、条件装配注解 <dependen…

为什么要用虚拟时钟Virtual clock?

通常RTL设计要求对芯片/module的输入信号进行reg_in打拍处理&#xff0c;对芯片/module的输出也要求做reg_out打拍处理&#xff0c;这是良好的代码习惯&#xff0c;为时序收敛留下足够裕量&#xff0c;也避免顶层例化综合后的子模块时出现模块间IO时序不满足的情况。综合阶段可…

DNF手游攻略:角色培养与技能搭配!游戏辅助!

角色培养和技能搭配是《地下城与勇士》中提升战斗力的关键环节。每个职业都有独特的技能和发展路线&#xff0c;合理的属性加点和技能搭配可以最大化角色的潜力&#xff0c;帮助玩家在各种战斗中立于不败之地。接下来&#xff0c;我们将探讨如何有效地培养角色并搭配技能。 角色…

亿图图示——文本换行

一、点击文本框&#xff0c;选择更多 二、勾选文本换行