当前位置: 首页 > news >正文

新手小白学C语言——排序算法(冒泡、选择、插入)

目录

一、引言

二、三种基础排序方式

1.冒泡排序

(1)代码复现

(2)代码演算

(3)逻辑概括

(4)应用场景

2.选择排序

(1)代码复现

(2)代码演算

(3)逻辑概括

(4)应用场景

3.插入排序

(1)代码复现

(2)代码演算

(3)逻辑概括

(4)应用场景

三、对三种排序的总结与思考

1、核心算法逻辑差异(本质区别)

2、应用场景差异(选型核心依据)

3、三者的相通之处与内在联系

4、核心总结与思考

四、编者的话


一、引言

排序算法作为一种常用的算法,与我们生活的方方面面息息相关,无论是做编程题还是解决具体生活场景中的问题,排序都发挥着重要的作用。因此,在C语言的学习中,要重视排序算法的学习并将其灵活运用。

二、三种基础排序方式

1.冒泡排序

(1)代码复现

void Sort1(int arr[6]) { for (int i = 0; i < 6; i++) { for (int j = 0; j < 6 - i - 1; j++) { int temp; if (arr[j] > arr[j + 1]) { temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } for (int i = 0; i < 6; i++) { printf("%4d", arr[i]); } }

(2)代码演算

下面我们通过一组无序数列进行对冒泡排序的学习:

3 5 1 4 2 8

我们先按照上述代码将这个无序数组进行排列:

i = 0:(j:0-->4) j = 0: arr[j] = 3 arr[j+1] = 5 //故if条件不成立,不进行数字交换 j = 1: arr[j] = 5 arr[j+1] = 1 //if成立,arr[j]和arr[j+1]发生交换 j = 2: arr[j] = 5 arr[j+1] = 4 //if成立,arr[j]和arr[j+1]发生交换 j = 3: arr[j] = 5 arr[j+1] = 2 //if成立,arr[j]和arr[j+1]发生交换 j = 4: arr[j] = 5 arr[j+1] = 8 //故if条件不成立,不进行数字交换

因此,经过第一大轮交换,数组变为3 1 4 2 5 8 。

i = 1:(j:0-->3) j = 0: arr[j] = 3 arr[j+1] = 1 //if成立,arr[j]和arr[j+1]发生交换 j = 1: arr[j] = 3 arr[j+1] = 4 //if条件不成立,不进行数字交换 j = 2: arr[j] = 4 arr[j+1] = 2 //if成立,arr[j]和arr[j+1]发生交换 j = 3: arr[j] = 4 arr[j+1] = 5 //if条件不成立,不进行数字交换

因此,经过第二大轮交换,数组变为1 3 2 4 5 8 。

i = 2:(j:0-->2) j = 0: arr[j] = 1 arr[j+1] = 3 //if条件不成立,不进行数字交换 j = 1: arr[j] = 3 arr[j+1] = 2 //if成立,arr[j]和arr[j+1]发生交换 j = 2: arr[j] = 3 arr[j+1] = 4 //if条件不成立,不进行数字交换

因此,经过第三大轮交换,数组变为1 2 3 4 5 8 。

其实可以发现,到这里数组已经变成了有序数组,那么后面 i = 3 和 i = 4的情况就不再继续阐述了,原理与步骤同上述操作。

(3)逻辑概括

相邻元素两两比较,逆序就交换,每轮把最大未排序数“冒”到末尾,重复至有序。

(4)应用场景

对小规模数据和接近有序的数组进行排序。

2.选择排序

(1)代码复现

void Sort4(int arr[6]) { int Idx,i,j,temp; for(i = 0;i <5;i++) { Idx = i; for(j = i+1;j < 6;j++) { if(arr[j] < arr[Idx]) { Idx = j; } } if(Idx != i) { temp = arr[i]; arr[i] = arr[Idx]; arr[Idx] = temp; } } for(int i = 0;i < 6;i++) { printf("%4d",arr[i]); } }

(2)代码演算

我们依然通过这个无序数组按照代码进行演算:

3 5 1 4 2 8
i = 0:(Idx = 0 j:1-->5) j = 1: arr[Idx] = 3 arr[j] = 5 //if不成立,Idx不与j交换 j = 2: arr[Idx] = 3 arr[j] = 1 //if成立,Idx与j交换 j = 3: arr[Idx} = 1 arr[j] = 4 //if不成立,Idx不与j交换 j = 4: arr[Idx} = 1 arr[j] = 2 //if不成立,Idx不与j交换 j = 5: arr[Idx} = 1 arr[j] = 8 //if不成立,Idx不与j交换

因此,经过第一轮循环,Idx变成了2,故数组变为 1 5 3 4 2 8 。

i = 1:(Idx = 1 j:2-->5) j = 2: arr[Idx] = 5 arr[j] = 3 //if成立,Idx与j交换 j = 3: arr[Idx] = 3 arr[j] = 4 //if不成立,Idx不与j交换 j = 4: arr[Idx] = 3 arr[j] = 2 //if成立,Idx与j交换 j = 5: arr[Idx] = 2 arr[j] = 8 //if不成立,Idx不与j交换

因此,经过第二轮循环,Idx变成了4,故数组变为 1 2 3 4 5 8。

i = 2:(Idx = 2 j:3-->5) j = 3: arr[Idx] = 3 arr[j] = 4 //if不成立,Idx不与j交换 j = 4: arr[Idx] = 3 arr[j] = 5 //if不成立,Idx不与j交换 j = 5: arr[Idx] = 3 arr[j] = 8 //if不成立,Idx不与j交换

因此,我们发现,第三轮循环已经没有交换出现了,故后续i = 3和i = 4不再赘述。

(3)逻辑概括

每次从未排序区间选出最小值,放到已排序区间末尾。

(4)应用场景

对数据量很小的数据进行排序。

3.插入排序

(1)代码复现

void Sort2(int arr[6]) { for (int i = 1; i < 6; i++) { if (arr[i] < arr[i - 1]) { int temp = arr[i]; int j; for (j = i - 1; j >= 0&&(arr[j] > temp); j--) { arr[j+1] = arr[j]; } arr[j+1] = temp; } } for (int i = 0; i < 6; i++) { printf("%4d", arr[i]); } }

(2)代码演算

我们依然通过这个无序数组按照代码进行演算:

3 5 1 4 2 8

下面进行推演:

3 5 1 4 2 8 i = 0: 3 5 | 1 4 2 8 temp = 1 □ 3 5 | 4 2 8 temp小于3 5 ,故3 5 都后移一位 1 3 5 4 2 8 i = 1: 1 3 5 | 4 2 8 temp = 4 1 3 □ 5 | 2 8 temp小于5 ,故5后移一位 1 3 4 5 2 8 i = 2: 1 3 4 5 | 2 8 temp = 2 1 □ 3 4 5 | 8 temp小于3 4 5 ,故3 4 5 都后移一位 1 2 3 4 5 8 i = 3 此时,arr[i] > arr[i - 1]故排序结束

(3)逻辑概括

把未排序元素依次插入到前面有序序列的正确位置。

(4)应用场景

对数据量小和接近有序的数组进行排序。

三、对三种排序的总结与思考

冒泡、选择、插入是最基础的三种内部排序算法,三者时间复杂度均为O(n²),属于简单暴力的排序算法,核心均通过双层循环完成有序化,但在核心逻辑、操作方式、性能特点和应用场景上存在显著差异,同时底层设计又具备高度相通性,以下从区别、场景、关联三方面整体总结。

1、核心算法逻辑差异(本质区别)

三者最核心的不同,在于每轮排序的目标和操作方式完全不同:

1. 冒泡排序:相邻交换,倒逼极值归位

通过相邻元素两两对比,逆序则交换,每一轮遍历都会将当前未排序区间的最大值“冒泡”到区间末尾,通过多次交换逐步实现整体有序。核心是「边比较、边交换」,交换次数最多。

2. 选择排序:先找极值,定点归位

全程遍历未排序区间,先找出区间内的最小值(最大值),仅用一次交换,将极值放到已排序区间末尾。核心是「多比较、少交换」,比较次数多,交换次数极少。

3. 插入排序:逐个插入,构建有序区间

默认数组前段为有序区间,依次取出未排序区间的元素,向前遍历,插入到有序区间的对应正确位置。核心是「移位插入、逐步构建有序」,无无效比较,贴合有序数组特性。

2、应用场景差异(选型核心依据)

三者虽都不适合大规模乱序数据,但根据自身性能特点,适配场景各有侧重:

1. 冒泡排序:仅适用于数据量极小、数组接近有序的场景,优化版冒泡可提前终止循环,效率最优;主要用于算法入门教学,实际项目极少使用,交换开销过高。

2. 选择排序:适配交换操作成本极高的场景(如复杂对象排序),凭借最少的交换次数降低性能损耗;不依赖数组有序度,稳定性差,适合小规模无序数据。

3. 插入排序:三者中实际可用性最高,数据量小、数组基本有序时效率远超另外两者;同时是唯一适配链表排序的算法,也是高级排序(快排、归并)的底层优化辅助算法。

3、三者的相通之处与内在联系

1.复杂度与层级相通:三者均为简单排序,平均、最坏时间复杂度均为O(n²),空间复杂度均为O(1),属于原地排序,无需额外开辟内存,仅依靠原数组完成排序。

2.排序思想同源:均采用「分区间排序」思路,将数组划分为已排序区间+未排序区间,通过多轮遍历,不断缩小未排序区间、扩大已排序区间,直至整体有序。

3.迭代逻辑一致:底层均依赖双层循环实现,外层循环控制排序轮数(确定已排序区间长度),内层循环负责未排序区间的比较、交换或移位操作。

4、核心总结与思考

从算法优劣来看,常规场景:插入排序 > 选择排序 > 冒泡排序。冒泡排序冗余交换最多,性能最差;选择排序牺牲比较次数换取最少交换,适配特殊场景;插入排序利用数据有序性,容错率最高、实用性最强。

三种基础排序的设计,体现了排序算法的核心优化思路:从“盲目交换”(冒泡),到“减少无效交换”(选择),再到“利用已有有序性、减少无效遍历”(插入),是算法从粗糙到精细的迭代过程。同时,三者的分区排序思想,也是后续快速排序、归并排序等高效分治算法的基础逻辑雏形。

此外,三者的特性也印证了算法选型的核心原则:没有最优算法,只有最适配场景的算法,需根据数据规模、有序程度、操作开销(比较/交换)灵活选择。

四、编者的话

小编作为安工大25级物联网工程学生,学习三种基础排序让我收获很多。

三者虽都是O(n²)算法,但设计思路、适用场景差异明显,也让我明白算法没有绝对好坏,要结合硬件资源取舍,其中插入排序更贴合物联网有序传感器数据处理需求。简单排序是高阶算法的基础,代码细节直接影响嵌入式设备功耗与效率。

希望以后能跟大家一起坚持手写推演吃透基础,结合单片机实验落地代码,同步刷题进阶分治、轻量化算法,依托竞赛与大创项目,把算法知识融入物联网软硬件开发,实现自己的专业梦!

PS:小编对算法类内容接触较少,也是刚开始系统性学习这方面的知识,如有不当,请您批评指正,小编将不尽感激,谢谢您!

http://www.cnnetsun.cn/news/3525792.html

相关文章:

  • HTTP和HTTPS
  • AI会话越多越忙?搭建首个自改进业务代理团队的完整实践
  • 微信支付证书自动化管理:Java平台证书下载工具深度解析
  • Relative Rotation Graph(相对旋转图)|Highcharts创建RRG 图示列代码
  • Aperture多语言SDK使用教程:Go、Java、Python、JavaScript全攻略
  • Golang学习-sync.RWMutex 读写锁
  • 物理AI迎来“开悟时刻”:大晓发布开悟世界模型、以人为中心的环采方案2.0与三大行业解决方案
  • 炸裂!WhatsApp再诉NSO:一纸“永久禁令”为何沦为废纸?全球“合法监听”黑产链调查
  • 金额用 Long 还是 BigDecimal?
  • grunt-contrib-copy 配置文件详解:从基础配置到高级选项
  • 【限时解密】头部MCN私有数字人直播中台架构图:含ASR延迟<200ms的FPGA加速方案与商用授权清单
  • fluxsort基准测试:在不同数据集上的表现分析
  • KnpGaufretteBundle社区贡献指南:如何参与项目开发与维护
  • Bazel rules_foreign_cc快速入门:5分钟学会构建外部C/C++项目
  • Verilog飞行射击游戏设计与实现:从敌机生成到子弹碰撞
  • 程序员誓言Programmer‘s Oath:保护用户隐私的7个关键原则
  • kableExtra在企业级应用中的应用:构建自动化报表系统的完整方案
  • ES6-learning:模块化编程的终极指南 - 掌握现代JavaScript开发的核心技能
  • McBSP多通道选择与帧同步:TDM通信原理与实战配置详解
  • physx-rs源码探秘:Rust绑定NVIDIA PhysX的实现原理
  • Slug性能优化技巧:提升URL短链服务响应速度的10个方法
  • 英雄联盟Akari助手:免费开源的终极游戏效率工具,快速提升你的操作水平
  • HarmonyOS7 同级分组菜单:用 MenuItemGroup 把菜单分层做得更顺手
  • Unity安卓打包全攻略:从环境配置到自动化构建的实战指南
  • TVA与世界模型共创具身智能“类脑想象力”(7)
  • 先计划,再动刀,Claude Code 的 plan 模式为什么适合严肃工程项目
  • MOSS-Music-8B-Thinking-6bit核心组件详解:音频编码器与语言模型融合
  • HarmonyOS7 跨层购物车页实战:@Provide 与 @Consume 不只是会用,还要用得顺手
  • Apache Commons Collections 与Java Stream API:如何选择最佳数据处理方案
  • 067、3A联动与协同控制:曝光、对焦、白平衡的闭环优化