目录

从最简单的操作开始

如何利用这个原子操作实现一个具体的小目标?

我们来手动模拟一下:

如何从一个小目标扩展到最终目标?

代码的逐步完善

第一阶段:定义函数框架和我们需要的“原子操作”

第二阶段:实现“多趟”的逻辑(外层循环)

第三阶段:实现每一趟的“比较交换”逻辑(内层循环)

思考与优化


从最简单的操作开始

📌我们的目标: 将一个无序的数组,例如 [5, 1, 4, 2, 8],变成有序的 [1, 2, 4, 5, 8]

面对这个任务,最直接、最“笨”的思考方式是什么?不是去想什么宏大的整体策略,而是思考:

我能做的最小的、能让数组变得“更”有序一点点的操作是什么?

这个最小的操作就是:只看相邻的两个元素

比如,我们先看 [5, 1]。在最终排好序的数组里,1 肯定在 5 的前面。而现在它们的顺序是错的。怎么办?很简单:把它们换过来!✅

[5, 1, 4, 2, 8] -> 交换 51 -> [1, 5, 4, 2, 8]

这个 “比较相邻元素,如果顺序错了就交换” 的操作,就是冒泡排序算法最核心的原子操作。它非常简单,但通过重复这个简单的操作,我们就能完成整个排序的宏伟目标。


如何利用这个原子操作实现一个具体的小目标?

我们不能漫无目的地到处交换。我们需要一个策略。与其想着如何把整个数组排好序,不如先定一个更小、更容易实现的目标:

“我们能否通过这个原子操作,把数组中最大的那个元素,放到它最终应该在的位置上?”

答案是可以的。最大的元素最终应该在数组的最右边。我们怎么把它“弄”过去呢?

我们可以从数组的左边开始,一路向右,不停地执行我们的“原子操作”。

我们来手动模拟一下:

假设数组是 arr = [5, 1, 4, 2, 8],长度 n = 5

1. 比较 arr[0]arr[1]:

  • [ **5, 1** , 4, 2, 8]

  • 5 > 1,顺序错了,交换。

  • 数组变为: [ **1, 5** , 4, 2, 8]

2. 比较 arr[1]arr[2]:

  • [1, **5, 4** , 2, 8]

  • 5 > 4,顺序错了,交换。

  • 数组变为: [1, **4, 5** , 2, 8]

3. 比较 arr[2]arr[3]:

  • [1, 4, **5, 2** , 8]

  • 5 > 2,顺序错了,交换。

  • 数组变为: [1, 4, **2, 5** , 8]

4. 比较 arr[3]arr[4]:

  • [1, 4, 2, **5, 8** ]

  • 5 < 8,顺序是正确的,什么都不做。

  • 数组保持: [1, 4, 2, 5, 8]

经过这样从左到右的一整轮操作,我们观察结果 [1, 4, 2, 5, 8]。我们成功了吗?

是的!最大的元素 8 已经被我们“护送”到了数组的最末端,也就是它最终应该在的位置。

这个过程就像水中的气泡,最大的那个总是最先“咕噜咕噜”地冒到水面。这就是“冒泡排序”这个名字的由来。我们把这样一整轮从头到尾的比较交换过程,称为一趟 (Pass)


如何从一个小目标扩展到最终目标?

我们已经完成了一趟,成功地将n个元素中最大的一个归位了。现在数组是 [1, 4, 2, 5, | 8] (用 | 把已归位的元素隔开)。

接下来怎么办?

很简单,我们把已经归位的 8 忽略掉,把前面的 [1, 4, 2, 5] 看作是一个规模更小的新问题

我们只需要对这个新的、长度为 n-1 的数组,重复刚才一模一样的操作

👉 我们来模拟第二趟:

  1. 比较 arr[0]arr[1]: [ **1, 4** , 2, 5, | 8] -> 1 < 4,不交换。

  2. 比较 arr[1]arr[2]: [1, **4, 2** , 5, | 8] -> 4 > 2,交换 -> [1, 2, 4, 5, | 8]

  3. 比较 arr[2]arr[3]: [1, 2, **4, 5** , | 8] -> 4 < 5,不交换。

第二趟结束后,数组变为 [1, 2, 4, | 5, 8]。看,现在次大的元素 5 也归位了!

📈 这个逻辑可以一直重复下去。

  • 第三趟,对 [1, 2, 4] 操作,会把 4 归位。

  • 第四趟,对 [1, 2] 操作,会把 2 归位。

  • 当只剩下 1 的时候,它自然就在正确的位置了。

我们需要多少趟呢?对于一个有n个元素的数组,最多需要 n-1 趟,就能把所有元素都排好序。

这个“重复执行”的思路,在编程里天然就对应着循环✅。


代码的逐步完善

现在,我们把上面的推导翻译成代码。

第一阶段:定义函数框架和我们需要的“原子操作”

我们知道肯定需要一个排序函数,并且排序过程中必然要用到“交换”这个原子操作。

// C/C++ 中,要在函数内部修改外部变量的值,需要使用指针
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}// 冒泡排序函数的整体框架
// arr 是要排序的数组,n 是数组的长度
void bubbleSort(int arr[], int n) {// 排序逻辑将在这里实现
}

第二阶段:实现“多趟”的逻辑(外层循环)

根据我们的推导,需要重复执行 n-1 趟排序。所以我们需要一个循环来控制这个“趟数”。

void bubbleSort(int arr[], int n) {// 这个外层循环控制总共需要多少“趟” (Pass)// i 表示已经有多少个元素在数组末尾归位了for (int i = 0; i < n - 1; ++i) {// 在这里实现每一趟具体的比较和交换逻辑}
}

第三阶段:实现每一趟的“比较交换”逻辑(内层循环)

在每一趟中,我们从数组的第一个元素开始,向后两两比较。 关键点:比较到哪里为止?

  • 第一趟 (i=0),n个元素都未排序,要比较 n-1 次,检查到 arr[n-2]arr[n-1] 为止。

  • 第二趟 (i=1),最后1个元素已归位,只需要处理前面 n-1 个元素,比较 n-2 次,检查到 arr[n-3]arr[n-2] 为止。

  • i 趟,最后 i 个元素已归位,比较范围是 n-1-i 次。

这个逻辑正好可以用另一个循环,也就是内层循环来实现。

#include <iostream> // 为了方便打印结果void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}void bubbleSort(int arr[], int n) {// 外层循环,控制“趟数”for (int i = 0; i < n - 1; ++i) {// 内层循环,控制每一趟中的“比较与交换”// 范围是 n - 1 - i,因为每过一趟,末尾就多一个排好的数for (int j = 0; j < n - 1 - i; ++j) {// 这就是我们的“原子操作”if (arr[j] > arr[j+1]) {swap(&arr[j], &arr[j+1]);}}// 我们可以加一句打印,来观察每一趟之后的结果std::cout << "第 " << i + 1 << " 趟排序后: ";for(int k=0; k<n; ++k) std::cout << arr[k] << " ";std::cout << std::endl;}
}

至此,一个可以正确工作的冒泡排序算法就完成了。它完美地复现了我们从第一性原理出发的推导过程。

复杂度分析

最好情况(数组有序):只需要一趟扫描,比较 n−1 次,没有交换 → 时间复杂度: O(n)

最坏情况(数组逆序):每次都需要比较并交换 → 时间复杂度: O(n^2)

空间复杂度:冒泡排序是 原地排序,只需常数个辅助空间 → O(1)

稳定性

冒泡排序是 稳定的

  • 原因:只有在 a[j] > a[j+1] 时才交换,如果相等,不动。

  • 因此相同元素的前后顺序不会被破坏。


思考与优化

我们的算法已经能用了,但它是不是最聪明的?有没有什么情况下它做了“无用功”?

💡设想一个情况:arr = [1, 2, 3, 5, 4]

  • 第一趟后,45交换,数组变为 [1, 2, 3, 4, 5]

  • 此时数组已经完全有序了。

但是,我们上面的代码并不知道这一点。它会继续傻傻地执行第二趟、第三趟、第四趟,虽然在这些趟中一次交换都不会发生。这显然是浪费。

优化的第一性原理: 如果我们发现某一趟从头到尾走下来,一次交换都没有发生,这说明了什么?

这说明数组中每一个元素都已经不大于它的后一个元素了,也就是说,整个数组已经完全有序了!

那么,我们就可以提前结束排序,而不必执行后面多余的趟数。

最终阶段:完善代码,加入优化

我们可以在每一趟开始前,设置一个标志位 swapped = false。如果在这一趟中发生了交换,就把它设为 true

一趟结束后,检查这个标志位。如果它仍然是 false,说明没发生任何交换,我们就可以直接 break 退出外层循环。

// 优化后的冒泡排序
void bubbleSortOptimized(int arr[], int n) {// 外层循环,控制“趟数”for (int i = 0; i < n - 1; ++i) {bool swapped = false; // 设立标志位// 内层循环for (int j = 0; j < n - 1 - i; ++j) {if (arr[j] > arr[j+1]) {swap(&arr[j], &arr[j+1]);swapped = true; // 只要发生一次交换,就将标志位置为true}}// 检查标志位:如果在一整趟中都没有发生交换,说明已经有序if (swapped == false) {std::cout << "在第 " << i + 1 << " 趟后提前结束。" << std::endl;break; // 退出外层循环}std::cout << "第 " << i + 1 << " 趟排序后: ";for(int k=0; k<n; ++k) std::cout << arr[k] << " ";std::cout << std::endl;}
}

这样,我们就从最基本的“交换相邻错误元素”这个原子操作出发,通过“完成小目标 -> 重复操作 -> 发现冗余 -> 加入判断”这一系列逻辑严谨的推导,最终构建出了一个完整且经过优化的冒泡排序算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/920915.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/920915.shtml
英文地址,请注明出处:http://en.pswp.cn/news/920915.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

教育项目管理工具新趋势:可视化与自动化如何提升效率?

课程项目不同于普通商业项目&#xff0c;它涉及 “教研设计→内容开发→师资准备→市场推广→学员服务” 全链路&#xff0c;环节多、角色杂、周期跨度大。传统的 Excel 表格、口头沟通不仅难以追踪进度&#xff0c;更易造成信息断层。而看板工具凭借 “可视化流程、轻量化协作…

计算两个二值图像的交集计算交点数量的基础上,进一步使用 DBSCAN 算法对交点进行聚

好的&#xff0c;如果你需要在计算交点数量的基础上&#xff0c;进一步使用 DBSCAN 算法对交点进行聚类&#xff0c;以合并距离较近的点&#xff0c;可以按照以下步骤实现&#xff1a; 计算交点&#xff1a;使用 cv2.bitwise_and 计算两个二值图像的交集&#xff0c;并提取交点…

Linux中的IP命令详解

华子目录 1.ip命令是什么1.1ip命令的由来1.2ip命令的安装包1.2ip选项&#xff08;基本不用&#xff09; 2.查看网络信息2.1显示全部网络接口信息2.2显示单个网络接口信息2.3显示单个接口状态2.4查看路由表2.5查看arp缓存 3.设置网卡ip地址3.1启用或停用网卡3.2设置默认网关3.3新…

如何解决pip安装报错ModuleNotFoundError: No module named ‘tox’问题

【Python系列Bug修复PyCharm控制台pip install报错】如何解决pip安装报错ModuleNotFoundError: No module named ‘tox’问题 摘要 在使用 PyCharm 2025 控制台执行 pip install 命令时&#xff0c;开发者经常会遇到如下错误&#xff1a; ModuleNotFoundError: No module nam…

拆分TypeScript项目的学习收获:处理编译缓存和包缓存,引用本地项目,使用相对路径

最近需要将工作中的一个TS包拆出一部分代码&#xff0c;以便在多个团队和项目中共享。原以为这会是一项特别简单的工作&#xff0c;但是也花了两天才大致拆成功。因此记录一下&#xff0c;也给有类似需求的同学一点经验。 所拆项目的大致功能&#xff1a;整个项目的结构大致分为…

瑞芯微RK3576平台FFmpeg硬件编解码移植及性能测试实战攻略

本文介绍瑞芯微RK3576平台&#xff0c;FFmpeg硬件编解码移植及性能测试方法。 FFmpeg简介与实测数据 FFmpeg简介 FFmpeg是一套多媒体框架&#xff0c;能够解码、编码、转码、复用、解复用、流、过滤和播放数字音频、视频&#xff0c;提供了录制、转换以及流化音视频的完整解…

【网络安全入门基础教程】网络安全零基础学习方向及需要掌握的技能

最近总有同学问我&#xff0c;0基础怎么学网络安全&#xff1f;0基础可以转行做网络安全吗&#xff1f;网络安全有哪些学习方向&#xff1f;每个方向需要掌握哪些技能&#xff1f;今天给大家简单写一下。 我的回答是先了解&#xff0c;再入行。 具体怎么做呢&#xff1f; 首…

Altium Designer中的Net-Tie:解决多网络合并与电气隔离的利器

Altium Designer中的Net-Tie:解决多网络合并与电气隔离的利器 在复杂的PCB设计中,我们常常会遇到一些特殊的电气连接需求。例如,需要将两个或多个逻辑上独立但物理上需要连接的网络(如不同电源域的GND)在特定点进行连接(单点连接),同时又要保持其网络标识的独立性。 …

计算机毕设项目 基于Python与机器学习的B站视频热度分析与预测系统 基于随机森林算法的B站视频内容热度预测系统

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Spark、hadoop、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题…

百胜软件×OceanBase深度合作,赋能品牌零售数字化实践降本增效

8月28日&#xff0c;由OceanBase主办的“2025零售数据底座创新大会”在上海举行。大会重磅发布了由爱分析、OceanBase携手王歆、沈刚两位行业专家联合编制的《零售一体化云数据库白皮书》。白皮书系统梳理了从“大促流量应对”到“AI应用落地”的全流程方法论&#xff0c;并为不…

2025年Java在中国开发语言排名分析报告

引言 在软件定义世界的2025年&#xff0c;编程语言的战略价值已超越工具属性&#xff0c;成为产业数字化转型的核心支撑与开发者思维模式的延伸载体。TIOBE指数作为全球技术市场变化的重要晴雨表&#xff0c;通过追踪工程师分布、课程设置、供应商动态及搜索引擎数据&#xff0…

TDengine 日期时间函数 DAYOFWEEK 使用手册

DAYOFWEEK 函数使用手册 函数描述 DAYOFWEEK 函数用于返回指定日期是一周中的第几天。该函数遵循标准的星期编号约定&#xff0c;返回值范围为 1-7&#xff0c;其中&#xff1a; 1 星期日 (Sunday)2 星期一 (Monday)3 星期二 (Tuesday)4 星期三 (Wednesday)5 星期四 (T…

从RNN到BERT

目录 序列模型简介RNN循环神经网络LSTM长短期记忆网络Transformer架构BERT模型详解实践项目 序列模型简介 什么是序列数据&#xff1f; 序列数据是按照特定顺序排列的数据&#xff0c;其中元素的顺序包含重要信息。常见的序列数据包括&#xff1a; 文本&#xff1a;单词或字…

椭圆曲线的数学基础

一、引言 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography, ECC&#xff09;是现代公钥密码学的核心工具之一。 相比传统的 RSA&#xff0c;ECC 可以用 更短的密钥长度 提供 同等甚至更高的安全性&#xff0c;因此被广泛应用于区块链、TLS、移动设备加密等场景。 要理解…

从能耗黑洞到精准智控:ASCB2智慧空开重构高校宿舍用电能效模型

随着智慧校园建设不断推进&#xff0c;校园宿舍的用电管理面临着安全性、智能化与可视化的多重挑战。传统用电监控手段在数据采集、实时控制和故障响应方面存在明显不足。安科瑞ASCB2系列物联网断路器通过集成多种智能感知、保护控制与通信手段&#xff0c;为高校宿舍提供了一种…

前端学习——JavaScript基础

前面我们已经学习了前端代码的骨架——HTML和前端美化工具——CSS。但是作为界面与客户进行交互我们还需要一个语言工具——JavaScript。 因此实际上HTML、CSS、JavaScript三者是这样的关系&#xff1a; HTML: 网页的结构(骨) CSS: 网页的表现(皮) JavaScript: 网页的行为(魂) …

Ubuntu下的压缩及解压缩

一、Linxu 下常用的压缩格式 Linux 下常用的压缩扩展名有&#xff1a;.tar 、.tar.bz2、 .tar.gz 。 二、Windows 下 7ZIP 软件的安装 因为 Linux 下很多文件是 .bz2 &#xff0c; .gz 结尾的压缩文件&#xff0c;因此需要在 windows 下安装 7ZIP 软件。 7-Zip 三、Ubuntu…

金融数据安全

安全框架金融数据生命周期是指金融业机构在开展业务和进行经营管理的过程中&#xff0c;对金融数据进行采集、 传输、存储、使用、删除、销毁的整个过程。数据生命周期安全框架,遵循数据安全原则&#xff0c;以 数据安全分级为基础&#xff0c;建立覆盖数据生命周期全过程的安全…

Unity抖音小游戏快捷立项准备/改动

本文由 NRatel 历史笔记整理而来&#xff0c;如有错误欢迎指正。 1、熟读抖音接入文档&#xff0c;记录要点 Unity 小游戏接入指南_抖音开放平台 2、创建Git仓库&#xff0c;开通成员权限 美术目录&#xff0c;对程序、美术、策划全开 程序目录&#xff0c;对程序全开、对部…

Labview使用modbus或S7与PLC通信

一、modbus 1.使用VI Package Manager (VIPM)安装modbus库 2.安装好后如下显示会有Modbus Library 3.Master API作为客户端&#xff0c;如下有一个例程 4.Slave API作为服务端&#xff0c;如下有一个例程 上述两个例程是通过IP 127.0.0.1可以互相通信的。数据是一直存在服务端…