资讯动态

冒泡排序可视化:用24个数字讲透算法过程与Python实现

发布时间:2026/9/2 7:23:59 来源:尧图企业网站定制
冒泡排序可视化不是用来“欣赏”的它是把算法过程变成可观察状态的最好方法。这篇文章用24个数字作为排序样本把冒泡排序从相邻比较、逐趟扫描到每个元素沉底的过程完整拆开最后给出可以运行的最小可视化程序和排查思路。如果你正在学算法或者准备给学生或初学者演示数组排序可以直接照着做。很多第一次做排序可视化的人容易把注意力放在动画效果上结果排序逻辑还没跑通折腾了半天画布、颜色和刷新频率。更稳的顺序应该是先选定一组能讲清楚规律的输入数据手工推演前几趟再用最小代码做出可视动画最后才考虑扩展。下面按这个顺序拆开讲。1. 为什么选24个数字而不是5个也不是100个可视化最怕的事情是过程太快看不清或者过程太长没耐心。24个数字刚好卡在一个很舒服的区间。1.1 小样本看不出“扫描”的重复感如果只用5个数字比如[5, 3, 8, 1, 9]冒泡排序第一趟扫描完之后可能已经部分有序第二趟结束就可能整体有序。整个过程只有两三趟交换动作也不多。你能看到“发生了交换”但很难体会“每一趟都会确定一个元素的位置”这件事。教学时我曾经试图用更小的样本讲清楚冒泡排序结果很多学生以为冒泡排序只是“来回交换几次”不知道它每一轮都有明确的任务。原因不是学生理解力差而是样本太小规律重复的次数不够。重复行为一旦太少算法结构就被模糊掉了。所以可视化教学里样本数量不能太少。1.2 大样本又会让细节被淹没反过来如果直接用100个数字柱状图是能摆下看起来也热闹。但每一次相邻比较只影响两根柱子100根柱子排列在屏幕上单根柱子变化非常细微。动画演示时人眼很难跟踪“正在比较的是哪两根”因为柱子太密了高亮位置很容易丢失。还有一个实际问题100个数字在最坏情况下需要跑99趟比较次数接近5000次。如果你把每次比较都做成动画帧播放时间会非常长如果你为了提高速度而增大帧间隔每一步的视觉焦点又会变得模糊。24个数字在普通屏幕上做柱状图时每根柱子宽度足够颜色变化清晰单帧信息量也容易读。它需要最多23趟可以完整展示前几趟后面跳着看也不会丢失核心规律。所以我一般会建议第一次做排序可视化时直接用 n24 起步。不是24有多特殊而是它在“看得清”和“看得完”之间找到了平衡点。1.3 完全逆序数据更能暴露算法过程数字数量确定之后接下来的问题是用什么样的初始数据。很多人习惯用随机数但随机数有一个问题可能排到中途已经接近有序提前退出动画戛然而止。这虽然展示了提前终止优化但对初学者来说核心过程还没看够。我建议先采用完全逆序的数据也就是从24排到1[24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]为什么选完全逆序因为在冒泡排序里逆序是最坏情况每一趟都会发生大量交换每一趟也必然会把一个当前最大元素放到正确位置。用这种数据能看到最多的交换过程也最能体现“沉底”规律。等理解透了再换成随机数据或接近有序数据观察算法在面对不同输入时的变化。2. 24个数字是怎么一步步排好的前5趟拆解这一章用完全逆序数组展示前5趟。为了排版清晰我直接用前后结果和省略号来表示省略号中间就是从大到小连续的整段数字。2.1 第一趟最大的24被送到最右边初始数组[24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]第一趟从第1个位置开始依次比较相邻两个数。因为后一个数总是比前一个数小所以每一对都会触发交换。比较完第1和第2个位置后23到左边24向右移一位继续比较第2和第3个位置22到左边24再向右移一位……这样一路交换到最右边24最终会出现在最后一个位置。第一趟结束后[23, 22, 21, ..., 2, 1, 24]这一步是整段动画里最关键的视觉信号最大的柱子像是“沉”到最右边了。可视化中如果这趟结束之后最右侧柱子不是当前未排序部分的最大值那排序逻辑就有问题。2.2 第二趟到第五趟次大、第三大依次沉底第一趟确定的是第24个位置也就是最后一个位置。第二趟只需要比较前23个元素范围缩小一格。过程完全一样比较大的元素不断右移最终23会到达倒数第二个位置。第二趟结束后[22, 21, 20, ..., 2, 1, 23, 24]第三趟、第四趟、第五趟的结果分别是[21, 20, 19, ..., 2, 1, 22, 23, 24] [20, 19, 18, ..., 2, 1, 21, 22, 23, 24] [19, 18, 17, ..., 2, 1, 20, 21, 22, 23, 24]看到这个规律可视化里就可以做一件事每一趟结束之后把已经沉底的右侧柱子改成另一种颜色。这样观众会非常直观地看到“排序进度条”在从右向左推进。这种效果其实很容易实现。每次外层循环结束记录当前已经确定的位置给这部分柱子换色即可。它不改变算法本身但会让“每趟确定一个最大值”这个规律非常明显。2.3 为什么只需要23趟而不是24趟全部排序完成后最后剩下一个元素1它已经在最左边不需要再比较。所以n个数的冒泡排序外层循环只需要跑n-1趟24个数字就是23趟。如果你写成外层循环范围是range(n)普通排序结果是没错的但最后一趟会多一次无意义的扫描动画里也会多出一段没有实际变化的“空帧”。另一个常见问题是内层循环的右边界为什么要递减。第一趟比较前23对也就是从下标0到22分别和后面的1到23比较第二趟只需要比较前22对因为第24个位置已经定死。写成代码时内层循环范围是range(n - 1 - i)。这个边界直接决定可视化中高亮的位置会不会跑到已排序区域。如果高亮经常进入右侧有色区域说明边界写错了。3. 自己写最小可视化程序从终端字符版到柱状图动画手工推演只能解决“理解逻辑”的问题要真正看到过程还需要写一点点代码。我的建议是分两步先做终端字符版再做柱状图动画。3.1 终端字符版先验证排序逻辑终端字符版不需要任何图表库只需要把数字打印成高度不同的字符条。例如24就打印24个#1就打印1个#。每一趟结束后打印一次就能直观看到柱子高度变化。import time def show(arr): for v in arr: print(# * v) print( * 40) data list(range(24, 0, -1)) n len(data) for i in range(n - 1): swapped False for j in range(n - 1 - i): if data[j] data[j 1]: data[j], data[j 1] data[j 1], data[j] swapped True show(data) time.sleep(0.5) if not swapped: break运行之后你会看到终端里从高到低的柱状结构每跑一趟右侧就会多一块较高的区域。这个版本的优点是零依赖只要有Python就能跑。缺点是只能在终端里刷新固定位置演示效果比较粗糙。为什么要先做这个版本因为它把“排序逻辑”和“可视化展示”分开。如果这段脚本最后输出的数组不是从小到大问题一定出在排序逻辑本身不会跑到绘制动画的环境里。先解决逻辑问题再做视觉优化是性价比最高的顺序。3.2 Python柱状图动画用matplotlib把过程变成动画确认终端版能排好后再把输出换成真正的柱状图动画。matplotlib是Python里比较常用的绘图库用来做教学演示足够。pip install matplotlib安装后可以用下面的代码做一个24个数字的冒泡排序动画import matplotlib.pyplot as plt import matplotlib.animation as animation def bubble_sort(arr): a arr[:] n len(a) for i in range(n - 1): swapped False for j in range(n - 1 - i): if a[j] a[j 1]: a[j], a[j 1] a[j 1], a[j] swapped True yield a, j, j 1, swapped if not swapped: break def update(frame_data): a, left, right, swapped frame_data bars ax.patches for idx, val in enumerate(a): bars[idx].set_height(val) bars[idx].set_color(#1f77b4) bars[left].set_color(red) bars[right].set_color(red) return bars n 24 data list(range(n, 0, -1)) fig, ax plt.subplots() ax.bar(range(n), data, color#1f77b4) ani animation.FuncAnimation( fig, update, framesbubble_sort(data), interval50, repeatFalse ) plt.show()这段代码有几个点值得说明。第一bubble_sort被写成了生成器函数。每次遇到yield程序暂停一次把当前数组状态、正在比较的两个位置传出去。FuncAnimation每次取一帧就调用一次update。这样排序过程就变成了动画帧序列。第二interval50表示每帧间隔50毫秒。数字越小动画越快越大动画越慢。对24个数字来说50毫秒左右观感比较舒服如果是较小屏幕可以调到80毫秒。第三bars[left].set_color(red)把正在比较的两个柱子标红。这样观众能清楚看到每一帧高亮的始终是相邻的两根柱子而不是随机跳跃的两根。3.3 网页版和其他可视化工具什么时候值得做如果只是教学或自学上面的Python版本就够了。如果你想把排序过程放到网页上交互操作可以换成HTMLCanvas或者现有的前端动画库。过程比Python版稍麻烦需要处理画布绘制、请求动画帧或定时器还要考虑不同浏览器宽度下的柱子间距。这里顺便说一个容易混淆的问题日常听说的“可视化大屏”“数据库可视化工具”“Kafka可视化”大多是把结果、指标、日志、数据流变成图表或面板。它们关心的是数据分布和系统状态不是算法执行过程。排序可视化属于“算法过程可视化”核心是状态变化两者目标不同。做排序动画时不需要去套那些大屏工具从最简单的绘图库起步反而更直接。调试任何排序动画先确认最终输出数组再去看画面。画面问题往往出在数据同步而不是绘图代码。4. 动画跑起来之后应该看哪些指标有经验的排序可视化不是“看个热闹”它需要观察几类指标。24个数字的可视化正好可以演示这些指标。4.1 比较次数、交换次数、趟数冒泡排序的复杂度是O(n^2)但O(n^2)是一个宏观级别具体到24个数字我们可以看具体的数字。n24时最坏情况是完全逆序的排序需要比较的次数是23 22 21 ... 1 276交换次数在最坏情况下也是276次因为每比较一次都触发交换。如果输入是接近有序的数据比较次数仍然接近276但交换次数会大幅减少。这个差异在可视化里非常明显红色高亮一直在出现但柱子位置很少变化说明比较仍在继续交换却很少发生。很多教程只说“比较相邻元素”但可视化会提醒你比较和交换是两件事复杂度分析里也经常会区分它们。趟数方面24个数字最多23趟。完全逆序数据会跑满23趟如果有一趟完全没有交换后面的趟数就可以跳过动画也会提前结束。这是冒泡排序的一个优化点很多教科书版本没有写但你可以在可视化代码里加上。下面这个表可以作为观察时的核对标准指标含义24个完全逆序数字下的预期情况外侧循环趟数当前已确定的沉底元素个数最多23趟比较次数相邻比较发生次数最多276次交换次数柱子位置交换次数最坏276次接近有序时远小于276高亮位置正在比较的两个下标始终是相邻的j和j1每趟右侧区域已排序区每趟结束后增加一个值4.2 稳定性在动画中怎么验证冒泡排序是稳定排序因为交换条件用的是严格大于。如果两个元素相等data[j] data[j1]不成立不会交换它们的位置所以相等元素的相对顺序能保持。在数字柱状图里如果所有柱子高度都不一样稳定性看不出区别。想验证稳定性可以给相同数值的柱子标上不同颜色。比如n24时把几个数字设为重复值相同数字使用同一基础色但不同标记。排序完成后检查这些相同值在前后的相对位置是否发生变化。如果唯一标识的顺序被颠倒说明排序逻辑可能错误地用了。可视化稳定性是课堂里特别好用的一环。因为单纯讲“稳定排序保持相对顺序”很抽象一旦在动画里给相同数字做标记观众能直接看到结果。这也算一个很好的排查思路如果动画结束时两棵同色柱子顺序发生变化优先检查比较条件是不是写成了。5. 排序动画没按预期工作时的排查链路做排序可视化最容易遇到的问题反而不是视觉特效而是排序逻辑和状态更新之间不一致。5.1 现象一动画播放完毕柱子仍然是乱序的先别急着改绘图代码。按照下面的顺序排查。首先把排序函数单独拿出去在命令行打印最终数组确认排序算法本身是否正确。不要用动画结果来判断因为动画可能只是帧数据取错了。其次确认生成器里用的是深拷贝还是原列表。如果外部列表和排序内部列表是同一个引用动画播放时可能一边更新一边被外部循环改写导致画面混乱。建议在函数入口写a arr[:]让排序过程操作副本。然后检查内层循环边界。错误边界不会每次都导致排序失败但会多出一大段无意义扫描甚至把已排序区域再次卷入比较。最后再检查动画更新函数是否真的拿到了每帧的数组状态。如果是用matplotlib要注意BarContainer中的柱子顺序和数组下标是否一致。5.2 现象二动画一闪而过或者卡死没有响应如果是“一闪而过”多是因为interval太小或者是生成器没有在每次比较后yield一次导致整个排序过程瞬间执行完动画帧只有少数几帧。把interval调到80毫秒左右并在每个相邻比较后yield一般能看到清晰过程。如果是“卡死”先看终端有没有报错。常见原因包括生成器里某个位置陷入死循环外层循环或内层循环边界写错或者FuncAnimation传入了无限生成器且没有设置frames上限。24个数字的排序是一个有限过程正常情况下生成器执行完所有yield后动画会停止。如果动画一直不结束说明生成器没有正常退出优先检查break条件是否放在正确位置。5.3 更隐蔽的问题柱子高度更新了但是颜色顺序混乱这种情况通常发生在有重复元素时。动画更新函数里如果只按数组值来设置颜色可能把相同高度的柱子颜色弄混。建议始终用柱子下标来更新高度和颜色。ax.patches[idx]对应下标idx不要用值去匹配柱子。排查顺序可以统一记成先打印最终数组再检查边界条件再看看生成器是否每帧yield最后检查绘图更新时用的是下标还是值。绝大多数排序可视化问题都能在这四步里找到。6. 24个数字之后往哪个方向扩展当你能把24个数字的冒泡排序可视化跑通接下来就可以往几个方向做更深入的扩展。6.1 换数据集随机、接近有序、完全逆序把初始数据从完全逆序换成随机数据你会看到动画后半段提前结束因为某一趟没有交换。这是观察“提前终止优化”的最好方式。再换成接近有序的数据比如[1,2,3,...,23,24]但随机打乱其中相邻几个动画会非常短但交换几次就完成。这种对比能让学习者感受到最坏情况、最好情况和平均情况在动画时长上的差异。在教学演示中我建议准备三组数据。不要只有一组随机数。三组数据能帮助观众理解算法复杂度不只是一个理论值而是真实体现在执行时间上的。6.2 扩展排序算法对比选择排序和插入排序冒泡排序的可视化理解了之后可以继续做选择排序和插入排序的可视化。三者的关键差异很直观冒泡排序是相邻比较大的元素像气泡一样往右边移动选择排序每趟扫描找最小值但只交换一次插入排序则是把当前元素向左插入到已排序区间的合适位置。同样是O(n^2)但交换次数和视觉节奏完全不同。特别是插入排序它在接近有序的输入下表现非常好。如果把三者放在同一套可视化框架里对比学习者能很快理解为什么插入排序在“基本有序”场景下有用。6.3 区分算法过程可视化和数据可视化最后想提一点边界。排序可视化属于“过程可视化”它关注的是步骤、状态、变化。在日常开发里我们还会遇到大量“结果可视化”场景比如把数据库查询结果、系统日志、监控指标、消息队列积压情况做成图表。后者的应用工具通常叫“某某可视化工具”但它们要解决的问题和排序可视化不一样。如果一个初学者想通过排序动画入门可视化学不要一开始就去研究那些复杂的可视化平台或大屏配置。先把“一个数组状态怎么变成一帧画面”这件事弄清楚后续再学数据可视化、大屏配置会轻松很多。排序可视化最大的优势就是数据简单、逻辑清晰、现象稳定非常适合作为第一个可视化项目。回到标题里的问题24个数字是怎么排好的其实不是这24个数字有多特别而是冒泡排序让它们每次把当前最大元素沉到右边重复这个动作直到所有元素都到达正确位置。先跑通终端字符版再用matplotlib做动画最后根据比较次数、交换次数和稳定性去观察过程是学习这种算法比较踏实的一条路线。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价