
简介冒泡排序是最基础、最常被用作教学示例的排序算法之一。这份专业课件面向编程初学者、职业院校学生以及算法教学者旨在通过直观演示与代码示例帮助读者理解排序的基本原理和实现方法。课件从实际场景“明日之星英语演讲大赛”的评分排序引入结合扑克牌排序的比喻生动展示相邻元素逐轮比较、交换并将较大元素逐步“冒泡”至末尾的过程随后给出算法分析、程序填空、复杂度推导以及标志位优化等关键内容。PPT共14页结构清晰知识点覆盖排序意义、数组存储、双重循环控制、时间与空间复杂度以及稳定性说明。资源包内仅含1个PPTX文件体积约156KB轻便易用方便教师直接用于课堂演示或学生课后复习。目前已有190人学习下载适合作为入门排序算法时的第一份教学或自学材料。1. 冒泡排序课件这堂课为什么值得从头认真讲一遍搜索“冒泡排序算法PPT课件”的人八成不是要学冒泡排序本身而是要把它讲给别人听——给新人做培训、给学生上课、或者在算法面试前把基础排序重新捋一遍。冒泡排序的完整实现不到十个有效行找一份源码很容易难的是把这几行代码讲得不心虚为什么内层循环要减去 i、为什么它叫稳定排序、flag 优化到底优化了什么。这篇就按我做算法培训课件的思路从 C 语言、Python 实现到每一页 PPT 怎么排把冒泡排序这堂课完整拆一遍。适合要讲课的工程师、计算机基础课老师和正在准备算法面试的开发者。2. 把冒泡排序讲透两层循环、三语言实现与流程图骨架很多课件一上来就贴代码这是最劝退的讲法。讲课的第一步不是 PPT是脑子里先有一张可运行的排序过程每一轮怎么比较、怎么交换、哪些元素已经沉底、下一轮可以少看几个。这张图画清楚了后面所有代码都是它的翻译。2.1 从“最大数沉底”看本质比较与交换是唯一动作冒泡排序的想法特别朴素从左往右看一遍把相邻元素里大的那个往后挪一趟走完最大的数一定到了最后一位。第二趟再从左边开始走到倒数第二位次大的数也归位。反复 n-1 趟整个序列有序。这个“相邻比较 条件交换”的动作就是它的全部。没有额外数组没有递归没有分治。它的空间复杂度是 O(1)优点是好理解、好写、稳定缺点也很直白——比较次数固定是 n(n-1)/2 的量级数据一上万就开始吃力。这正是课堂上第一个值得讲透的排序它能让学生直观看见“排序的本质是比较与交换”而不是一上来就面对快排那种递归分治的黑匣子。2.2 C 语言最简实现两层 for 循环与边界参数先给课件里最常用的 C 语言版也是 C 语言课上标准写法void bubble_sort(int *arr, int n) { // i 表示已经沉底的元素个数也是已经完成的轮数 for (int i 0; i n - 1; i) { // 内层只需要比较到 n - 1 - i因为最后 i 个元素已经就位 for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; } } } }很多人背得出外层循环i n - 1但讲不出为什么。原因很简单n 个元素只需要排 n-1 轮因为每一轮至少让一个元素沉底前 n-1 个元素的位置确定后最后一个元素自动就位。内层循环j n - 1 - i是这堂课的第一个考点。它同时做了两件事保证j1不越界——因为最大下标是 n-2 i? 这里 i 从 0 开始n - 1 - i - 1 n - i - 2j 1最多到n - i - 1正好是“这一轮还没排序的最后一个元素”。另一个作用是跳过已经沉底的 i 个元素避免无意义的比较。交换用临时变量 tmp是 C 语言课的标准动作。这里不建议讲异或交换这类技巧容易把初学者绕进去和排序本身毫无关系。2.3 Python 和 Java 实现交换写法与传参差异Python 版会让代码短一大截适合在课件上做“算法思想展示”不建议当 C 语言的替代品讲而是当对照def bubble_sort(arr): n len(arr) for i in range(n - 1): # i 是已就位的元素个数内层比较区间逐步向左收缩 for j in range(n - 1 - i): if arr[j] arr[j 1]: # 并行赋值等号右侧先取值不存在中间变量覆盖问题 arr[j], arr[j 1] arr[j 1], arr[j] return arrPython 的并行赋值是这个实现里最值得讲的一句arr[j], arr[j1] arr[j1], arr[j]是先计算右侧两个表达式的值再统一赋值天然避开了 C 语言里必须引入 tmp 的问题。Java 没有并行赋值写法回到临时变量那一套public static void bubbleSort(int[] arr, int n) { for (int i 0; i n - 1; i) { for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; } } } }这里可以补一个面试常问的点Java 和 C 语言里数组作为参数传入函数时都会退化成指针或引用在函数里用sizeof(arr)/sizeof(arr[0])求长度是错的必须把长度 n 一并传入。这个细节放在第 4 章详细说。顺带提一句 C标准库没有提供bubble_sort这个算法。std::sort是内省排序std::stable_sort是归并排序它们在实际工程里覆盖了冒泡排序几乎所有的应用场景。面试里问到这里可以补一句“标准库不提供冒泡因为它平均复杂度被插入排序全面压制且没有额外亮点”这个回答比背源码加分得多。2.4 算法流程图怎么画三个框撑起课件第一张图课件里一定有一张算法流程图但很多人的图画错了把“交换”画成单独一个大流程学生看不出它和“比较”是绑定关系。标准画法是四个部分按顺序排开始框初始化 i 0进入外层循环判断框i n - 1是否成立不成立则结束初始化 j 0进入内层判断框j n - 1 - i不成立则 i 自增回到第 2 步比较框arr[j] arr[j1]成立则走交换动作不成立则跳过j 自增后回到第 3 步。这张图的视觉重点是第 4 步比较和交换要画在同一个分支里用向右的分支表示“满足条件才交换”向下的分支表示“不满足直接 j 自增”。很多课件在这里偷懒只画一个抽象的“冒泡过程”学生看完还是不知道代码怎么写。流程图旁边配一句话就够了“每轮把当前区间内最大的数移到区间末尾”。这句话是整张图的口头注释也是学生日后回忆冒泡排序的关键词。画完图再给代码代码就成了图的翻译不再是无根的一堆符号。3. 复杂度与优化什么时候冒泡排序反而比快排好用这一章是整堂课的分水岭。前两章讲“冒泡排序怎么写”这里讲“冒泡排序值不值得用”。很多课件在这块讲得含糊——只丢出一个 O(n²)然后说“所以它慢”。这不够。要讲就讲清楚O(n²) 是怎么算出来的哪些输入会让它变成 O(n)为什么有些场景下冒泡排序反而比快排更合适。3.1 时间复杂度图景最好、平均、最坏分别对应什么输入先算比较次数。第一轮比较 n-1 次第二轮 n-2 次直到最后一轮 1 次总比较次数是等差数列求和C (n-1) (n-2) ... 1 n(n-1)/2这个值是个固定数和输入数据是否有序无关。单纯比较次数这件事冒泡排序在三重 O(n²) 排序里算“量化宽松”的因为插入排序和选择排序同样有接近的常数但冒泡的交换动作多常数更大。交换次数就分情况了。最坏情况是逆序输入每一轮比较都触发一次交换交换次数也是 n(n-1)/2。最好情况是输入已经有序交换次数为 0。平均情况大约是 n(n-1)/4 次交换。所以完整的时间复杂度表是输入情况比较次数交换次数时间复杂度升序输入最好n(n-1)/20O(n²)逆序输入最坏n(n-1)/2n(n-1)/2O(n²)随机输入平均n(n-1)/2约 n(n-1)/4O(n²)注意一个细节即使是升序输入朴素冒泡排序的比较次数依然是 n(n-1)/2复杂度还是 O(n²)只是交换变少了。如果不加优化“最好情况 O(n)”是不成立的——这个点课件必须写清楚面试追问时常在这里埋坑。3.2 稳定排序这个属性为什么不交换相等元素前面代码里交换条件是arr[j] arr[j 1]注意是严格大于不是大于等于。这个选择带来了一个关键性质相等的元素不会发生位置交换相对顺序保持不变。这就是“稳定排序”的定义。数组 [3a, 2, 3b, 1] 中3a 和 3b 初始位置是 3a 在前排序后仍是 3a 在前。如果写成相邻相等元素也会交换稳定性就被破坏了。这个性质有什么用实际场景是“按多个字段排序”先按姓名排再按班级排第二次排序时稳定排序会保留第一次排序的结果。JavaScript 的Array.prototype.sort在不同引擎里稳定性不一致ES2019 之后才规定必须稳定Python 的list.sort和 Java 的Collections.sort都保证稳定。而 C 语言的qsort不保证稳定——这也解释了为什么工程里经常用归并排序替代快排不是快排慢是它不稳定。面试题“冒泡排序为什么稳定”答案只有一个关键词比较条件用不用。课件里可以加一行批注提醒学生看源码时先检查符号。3.3 三个能写进课件的优化flag、边界收缩、鸡尾酒排序第一个优化是 flag 提前退出。思路是某一轮从头到尾一次交换都没发生说明序列已经有序直接结束。这是教科书里最标准的冒泡优化也最容易理解def bubble_sort_flag(arr): n len(arr) for i in range(n - 1): swapped False for j in range(n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swapped True if not swapped: break return arr加了一个swapped标志位内层循环结束后检查它。如果为 False直接 break。这个优化让升序输入的复杂度从 O(n²) 降到 O(n)——只需扫描一遍确认没有相邻逆序立刻退出。实际收益取决于数据的有序程度基本有序的数据集上收益非常明显。第二个优化是边界收缩。原理是每一轮最后一次交换发生的位置 j说明 j1 及之后都已经有序下一轮内层循环只需跑到 j 就行。实现上把外层的for i改成while i n - 1内层每轮记录last_swap下一轮边界收缩到这个位置def bubble_sort_bound(arr): n len(arr) i 0 while i n - 1: swapped False last_swap 0 for j in range(n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swapped True last_swap j if not swapped: break i n - 1 - last_swap return arr这里i n - 1 - last_swap是把下一轮比较范围收缩到 last_swap 之前。比如 last_swap 为 2表示前三个位置0、1、2还需要检查后面的已经有序下一轮比较次数从 n-1-i 变成 3-12 次。和 flag 优化叠加后基本有序数据的比较次数会明显下降。第三个优化是鸡尾酒排序也叫双向冒泡一趟从左往右把最大数沉底下一趟从右往左把最小数冒到最前面交替进行。它适合“大部分数据已经有序只是中间有一段逆序”的输入能比标准冒泡少跑几轮。不过实现要处理左右两个边界代码长度翻倍课件里适合作为拓展题留给学生自己写。提示三个优化里flag 是必讲边界收缩是加分项鸡尾酒排序看课时。别把三个全塞进一页 PPT学生会记不住任何一个。3.4 和其他排序算法对比数据量多小才轮到冒泡课件里放一张对比表比讲十句话都有用排序算法平均复杂度最坏复杂度稳定性额外空间亮点冒泡排序O(n²)O(n²)稳定O(1)实现简单、稳定插入排序O(n²)O(n²)稳定O(1)常数小、基本有序时极快选择排序O(n²)O(n²)不稳定O(1)交换次数最少快速排序O(n log n)O(n²)不稳定O(log n)工程默认选择归并排序O(n log n)O(n log n)稳定O(n)稳定 可预测堆排序O(n log n)O(n log n)不稳定O(1)原地排序冒泡排序在什么场景下“反而好用”我一般会讲三个场景一是教学它是最适合展示“排序本质”的入门算法二是数据量很小比如十几个元素且基本有序用 flag 优化的冒泡代码简单程度完胜归并和快排三是面试基础轮面试官问排序稳定性的概念时冒泡是最天然的引子。但工程里不要指望它。排序 10 万个随机整数快排大概几十毫秒冒泡是几秒到十几秒的量级。这个对比值得在课件里放一个真实的运行时间学生才会有体感而不是只看复杂度符号。4. 避坑写代码和做课件最容易翻车的四个细节这一章是血泪经验。每次带新人、每次学生交上来的作业翻车几乎都集中在同样的四个地方。有的错在代码有的错在 PPT 讲法。逐条写清楚。4.1 内层循环写成 j n - 1越界与无效比较同时出现现象内层循环写成for (int j 0; j n - 1; j)代码不报错但运行结果不对或者某些语言直接数组越界。原因内层循环的终点必须是n - 1 - i。写成n - 1后每一轮都把整个数组比较一遍那些已经沉底的最大数又会被重复比较。更危险的是当 i 已经很大时比如最后一轮 i n-2j n-1仍然会跑到 j n-2访问arr[j1]就是arr[n-1]在边界处容易踩到未定义行为在 C 语言里可能碰巧没崩但结果完全不可控。解决把内层循环写成j n - 1 - i并给代码加一行注释“j 的最大值 待排序区间的倒数第二个位置”。讲课的时候用一个小数组现场推演一遍第一轮结束时最大数已经在最后第二轮开始就没人会去碰最后一个元素了。4.2 在函数里用 sizeof 求长度数组传参退化为指针现象新手把排序写成一个函数在函数内部用int n sizeof(arr) / sizeof(arr[0]);求数组长度结果 n 一直是 1 或者 8排序仿佛没发生。原因数组作为参数传入 C/C 函数时不会把整个数组复制进去而是退化成指向首元素的指针。sizeof(arr)在函数内部求的是“指针变量”的大小8 字节64 位系统不是整个数组的大小除以单个元素大小后就是 1。这是 C 语言里最经典的坑也是很多人从写 main 函数改成写排序函数时第一次遇到的“玄学问题”。解决要么把长度作为显式参数传入比如bubble_sort(arr, n)要么在调用方用sizeof(arr) / sizeof(arr[0])求好再传。在 C 里可以用模板或std::size但课件里不推荐展开讲——先把传参这个基础动作讲明白学生才会理解为什么所有排序函数都带着一个 n 参数。4.3 动画只做“比较”不做“交换”学生看两轮就懵现象PPT 用动画高亮两个相邻元素表示“在比较”但高亮完之后直接跳到下一组没有展示交换动作。学生看了两轮就懵了明明看到 5 和 3 在比较下一屏 3 就到前面去了中间过程呢原因做课件的人自己脑子里有完整的排序过程默认学生也能脑补出交换。但初学者跟不上这个跳跃。比较只是“判断”交换才是“改变数组状态”的动作漏掉交换整个排序过程在学生眼里是断裂的。解决每个比较动作配两个动画步骤第一步高亮两个格子表示“比较”第二步让两个数字交换位置PPT 里用“移动”动画或“切换位置”效果。这一步做完再高亮下一组。宁可动画慢一点也不能跳步。课件备注页写上比较和交换是一个不可拆分的动作单元。4.4 拿全有序数组测优化版复杂度结论严重失真现象为了演示 flag 优化的威力拿一个已经升序排列的数组去测优化版瞬间跑完于是下结论“优化后冒泡排序是 O(n)”。这个结论单独看没错但学生会误会成“冒泡排序经过优化就变快了”。原因flag 优化只对“基本有序”的输入有效最好情况确实降到 O(n)但最坏情况还是 O(n²)。拿升序数组测等于只展示了最好情况的数字掩盖了随机和逆序输入下的真实表现。解决课件里对比时至少跑三种输入——随机数组、升序数组、逆序数组——分别记录比较次数。用真实数字说话随机 1 万个整数朴素版和优化版的耗时差距很小升序数组优化版从约 5000 万次比较降到 9999 次逆序数组两版表现几乎一样。这样学生才理解flag 不是让冒泡变快是让它在“已经有序”时能提前下班。5. PPT 课件怎么搭13 页结构、演示数据与动画节奏代码和复杂度都讲透了剩下的是怎么把这些内容排进 PPT。有经验的讲师都知道课件不是代码的堆叠而是一条有节奏的叙事线。这里给出一套我用着顺手的 13 页结构以及每一页的讲解目标。5.1 课件骨架引例、伪代码、演示、复杂度四段主线一套完整的冒泡排序算法课件至少要覆盖四条主线引例、伪代码、演示、复杂度。引例解决“为什么要排序”伪代码解决“怎么描述算法”演示解决“它到底怎么跑”复杂度解决“它值不值得用”。很多课件缺了引例直接上伪代码学生不知道为什么学或者缺了复杂度学生学会了写法但不知道什么时候用。引例建议用“排队按身高调整”这个生活场景体育课整队老师让从矮到高站学生只能相邻两个人比身高、必要时换位置。这个例子天然对应相邻比较和交换不需要额外解释就能映射到代码。比起“把数组从小到大排序”这种抽象陈述生活化引例对初学者的启动成本低得多。伪代码放在代码之前。伪代码不需要考虑语法只需要写出核心逻辑外层循环控制轮数内层循环控制比较区间如果前一个大于后一个就交换。“先用自然语言把逻辑说清楚再给代码”这个顺序本身就是教学法。这一步做对了后面贴代码时学生会觉得“果然如此”而不是“又一段天书”。5.2 演示数据选 6 个元素一轮能看完交换、不交换和沉底演示数据的选取是课件设计里最容易被忽略、又最重要的一件事。选 3 个元素太少看不出“每轮把最大数沉底”的规律选 10 个以上太多动画节奏拖沓学生跟丢。我一般用 6 个元素[5, 1, 4, 2, 8, 3]。这组数据的妙处在于第一轮里同时出现了“交换”和“不交换”两种动作而且最大数 8 在一轮之内从位置 4 沉到位置 5学生能清楚看到沉底过程。演示时逐步走完第一轮第一轮从头到尾比较 5 次5 和 1 交换5 和 4 交换5 和 2 交换5 和 8 不交换8 和 3 交换。结束后数组变成[1, 4, 2, 5, 3, 8]8 沉底。第二轮只需要比较前 4 对1 和 4 不交换4 和 2 交换4 和 5 不交换5 和 3 交换。结束后是[1, 2, 4, 3, 5, 8]5 沉底。第三轮得到[1, 2, 3, 4, 5, 8]第四轮没有任何交换。这组数据还顺便演示了 flag 优化的意义第三轮结束后数组已有序第四轮跑完发现没交换提前退出。一次演示同时覆盖三种情况性价比极高。5.3 13 页课件怎么排每页只讲一个结论页码页面内容讲解目标1-2封面 目录抛出“相邻交换能否完成排序”的问题3引例排队按身高调整建立比较与交换的直观印象4问题定义与稳定性定义明确输入、输出、稳定性的含义5算法思想最大数沉底一句话概括排序过程6算法流程图把思想转成流程结构7一次完整演示6 元素数组看熟一轮的完整过程8第二轮与边界收缩观察已排序区间的变化9C 语言代码 Python 对照把流程图翻译成代码10复杂度分析比较次数、交换次数、时间复杂度表11优化flag 提前退出用“无交换即有序”解释优化原理12与其他排序算法对比快排、归并、插入、选择的全景对比13练习 小结布置逆序数组的手动排序作业这套结构的核心原则是“一页只讲一个结论”。第 7 页专门做演示第 9 页才给代码第 10 页只算复杂度。“代码 复杂度 优化”挤在同一页是新手做课件最常见的失败模式——页面信息密度一高课堂焦点就没了。第 13 页的练习建议是让学生手动模拟逆序数组[6, 5, 4, 3, 2, 1]的冒泡排序统计比较次数和交换次数。这个练习的答案正好对应最坏情况 O(n²)直接接上第 10 页的复杂度表形成闭环。5.4 动画三连先比较、后交换、再沉底PPT 动画的设计对算法演示至关重要。我的习惯是每个比较动作做“动画三连”顺序严格固定第一步高亮比较的两个格子第二步如果触发交换让两个数字平滑移动换位第三步把本轮沉底的最大数字置灰表示“以后不用再看它了”。第一个动作是“比较”。在 PPT 里给两个相邻文本框设置一个“强调”动画比如变色或加边框。第二步“交换”是平移动画两个格子向对方位置移动这个视觉动作直接模拟了内存中的赋值。第三步“沉底”是把最大数的颜色调暗让学生潜意识里把它从待排序区间里划掉。提示动画不是越多越好。每个动作只保留“高亮、移动、置灰”三个状态超过三个学生就会疲劳。速度统一设定为“中速”不要用弹跳、翻转这类花哨效果。6. 用随机数组与交换次数验证课件里的每一条结论课件做到这里内容已经很完整了。但每一页 PPT 里的数字我都建议先写脚本跑一遍验证再放上去。这里给一个我常用的验证脚本统计朴素版和 flag 优化版在三种输入下的比较次数与交换次数。6.1 一个小脚本自动统计比较次数与交换次数import random def run_test(arr, use_flagFalse): n len(arr) comp swap 0 i 0 while i n - 1: swapped False for j in range(n - 1 - i): comp 1 if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swap 1 swapped True if use_flag and not swapped: break i 1 return comp, swap random.seed(1) data [random.randint(0, 100) for _ in range(20)] cases [ (随机数据, data[:]), (升序数据, sorted(data)), (降序数据, sorted(data, reverseTrue)), ] for name, arr in cases: c1, s1 run_test(arr[:], use_flagFalse) c2, s2 run_test(arr[:], use_flagTrue) print(f{name}: 朴素版比较{c1}次/交换{s1}次, flag版比较{c2}次/交换{s2}次)这个脚本用计数器把内层循环的比较和交换都累加起来最后输出三个数据类型的对比。随机数据下两版表现接近升序数据下朴素版比较 190 次flag 版只要 19 次降序数据下两版都是 190 次没有区别。跑完你就知道课件里应该怎么写才严谨。6.2 把真实数字写进备注页课堂追问才接得住有了这个脚本课件每一页的复杂度结论都可以换成真实数字。比如第 10 页复杂度表的下方备注页写“升序 20 个元素朴素版比较 190 次flag 版 19 次”再补一行“降序 20 个元素两版都是 190 次因为 flag 检测不到有序区间就退不出来”。学生问“优化版是不是一定更快”时你直接把这个对比抛出来flag 的本质是识别有序不是降低常数。追问到这一步说明这堂课已经把排序算法的逻辑讲透了。我最早讲排序算法时曾在幻灯片上把内层循环边界写成j n当场被一个旁听的实习生指出来。从那以后我养成了一个习惯课件里的每一个复杂度结论、每一个优化收益都先跑一遍脚本把真实数字写进备注页再上台。这个习惯帮我少翻了好多回车。希望帮到你。本文还有配套的精品资源点击获取