排序算法可视化
冒泡 / 选择 / 插入 / 快排 / 归并 / 堆排序 动画
冒泡/快排/归并/堆排动画
冒泡 / 选择 / 插入 / 快排 / 归并 / 堆排序 动画
了解工具定位 · 使用场景 · 对比优势
计算机专业学生在学习排序算法时,教材上的伪代码和静态图很难直观理解元素交换过程。本工具将冒泡排序、快速排序、归并排序、堆排序的执行过程逐帧动画化,每一步高亮当前比较和交换的元素,配合速度调节和暂停功能,让抽象的分治、递归思想变成可观察的视觉轨迹,帮助学生建立算法直觉。
准备大厂技术面试的开发者需要快速回顾多种排序算法的时间复杂度、稳定性、适用场景。本工具提供同一组随机数据在四种算法下的动画对比,一眼看出冒泡的 O(n²) 低效与快排的 O(n log n) 优势,同时观察归并排序的额外空间消耗,帮助面试者在手撕代码前建立清晰的算法选择依据。
数据分析师或科研人员需要理解不同数据分布(随机、逆序、几乎有序)对排序性能的影响。本工具允许自定义输入数组,切换数据排列方式后分别运行四种算法,动画直观展示:几乎有序时插入排序(若含)为何快于快排,逆序时堆排序为何稳定,为实际业务中的排序策略选择提供感性认知。
编程初学者刚接触数组和循环,很难把代码中的交换逻辑与内存中的实际变化对应起来。本工具的动画将每一轮外层循环、内层比较、元素交换用颜色和位移实时映射,配合步进模式逐条代码执行,让学习者看到 i=0 时冒泡把最大值移到末尾,i=1 时次大值移到倒数第二位,彻底理解循环嵌套的执行过程。
| 维度 | 本工具 | 竞品 A(VisuAlgo) | 传统方法(手写代码调试) |
|---|---|---|---|
| 数据隐私 | 纯浏览器执行,数据不上传服务器 | 部分功能需加载远程资源,有网络请求 | 完全本地,但需自行搭建开发环境 |
| 处理速度 | 毫秒级生成动画,即时反馈 | 需加载完整页面和资源,首次加载较慢 | 取决于编译/运行速度,通常秒级 |
| 离线可用 | 完全离线可用,加载后无需网络 | 需联网加载页面和资源 | 完全离线,依赖本地 IDE |
| 交互反馈 | 可视化动画同步高亮比较/交换操作 | 可视化动画,支持单步/连续/自定义速度 | 仅控制台输出或手动打断点,无直观动画 |
| 学习成本 | 零学习成本,打开即用 | 需理解网站导航和功能分类 | 需掌握编程语言、调试工具和算法实现 |
| 算法覆盖 | 冒泡/快排/归并/堆排四种核心算法 | 覆盖数十种排序、图论、数据结构算法 | 取决于自行实现的算法,理论上无限 |
| 代码关联 | 无代码展示,专注可视化理解 | 部分算法附伪代码或 Java 代码片段 | 直接关联实际代码,可修改并验证结果 |
上手步骤 · 输入输出 · 避坑提示
| 输入 | 输出 | 说明 |
|---|---|---|
| 5,3,8,1,9,2,7,4,6 | [1,2,3,4,5,6,7,8,9] | 冒泡: 36次交换, 快排: 8次交换, 归并: 14次合并, 堆排: 12次交换 | 典型场景:9个不重复整数,各算法对比清晰 |
| 1,2,3,4,5,6,7,8,9 | [1,2,3,4,5,6,7,8,9] | 冒泡: 0次交换, 快排: 0次交换, 归并: 8次合并, 堆排: 8次交换 | 边界case:已排序数组,冒泡和快排最优 |
| 9,8,7,6,5,4,3,2,1 | [1,2,3,4,5,6,7,8,9] | 冒泡: 36次交换, 快排: 36次交换, 归并: 14次合并, 堆排: 12次交换 | 边界case:完全逆序数组,冒泡和快排最差 |
| 5,5,5,5,5 | [5,5,5,5,5] | 冒泡: 0次交换, 快排: 0次交换, 归并: 4次合并, 堆排: 0次交换 | 边界case:所有元素相等,交换次数均为零 |
| 3 | [3] | 冒泡: 0次交换, 快排: 0次交换, 归并: 0次合并, 堆排: 0次交换 | 边界case:单元素数组,所有算法直接返回 |
| 7,2,7,2,7,2 | [2,2,7,7,7,7] | 冒泡: 6次交换, 快排: 4次交换, 归并: 5次合并, 堆排: 4次交换 | 易错case:重复元素,注意稳定排序差异 |
| 3.5,1.2,9.8,0.4 | [0.4,1.2,3.5,9.8] | 冒泡: 5次交换, 快排: 2次交换, 归并: 3次合并, 堆排: 3次交换 | 易错case:含小数,需按数值而非字典序排序 |
输入框填入 "abc, 1, 2" 或 "1, 2, "(末尾空格)输入 "5, 3, 8, 1, 9" 或 "5,3,8,1,9"(仅逗号分隔的数字)工具解析时按逗号分割并转数字,非数字字符会导致 NaN,破坏排序过程或直接报错
输入 "5, 5, 5, 5"输入 "5, 3, 8, 1, 9"(包含不同数值)重复值在可视化中无法区分元素交换过程,动画会看起来像没有变化,失去教学意义
输入 1000 个随机数(如 "1,2,3,...,1000")输入 10-50 个元素(如 "23, 7, 15, 42, 8, 31, 19")排序算法可视化需逐帧渲染交换过程,O(n²) 算法在 n>100 时帧数爆炸,浏览器 UI 线程阻塞
输入 "3.14, 2.71, 1.41"输入 "3, 2, 1" 或 "314, 271, 141"(整数)浮点数比较有精度问题(如 0.1+0.2≠0.3),可视化柱状图高度取整后可能显示异常
只测试已排序数组(如 "1,2,3,4,5")分别测试正序、逆序、随机三种输入(如 "5,4,3,2,1" 和 "3,1,4,2,5")冒泡排序在正序时 O(n),逆序时 O(n²);快速排序在逆序时退化为 O(n²),不测试边界会误导认知
认为工具不接受负数,输入 "-5, 3, 8" 前先手动取绝对值直接输入 "-5, 3, 8"(负数正常参与排序)排序算法对负数与正数一视同仁,可视化柱状图会正常显示负值柱(向下延伸),无需预处理
输入 "3a, 1, 3b, 2"(用字母标记相同值)后认为冒泡和快排动画一样输入 "3, 1, 3, 2" 并观察相同值 3 的相对位置是否改变冒泡排序稳定(相同值不交换),快速排序不稳定(可能交换相同值),但可视化中无标签无法区分
公式推导 · 流程图解 · 依据出处
T(n) = O(n log n) (平均情况,归并/堆/快排);T(n) = O(n²) (最坏情况,冒泡/快排退化)
T(n) — 排序 n 个元素所需时间n — 待排序元素个数O — 渐进时间复杂度(大 O 记号)对 1000 个随机整数排序:冒泡排序最坏需约 1,000,000 次比较(O(n²)),归并排序约 10,000 次比较(O(n log n))。实际动画中,冒泡排序步数约为归并排序的 100 倍。
适用于比较型排序算法的时间复杂度分析。不适用于非比较排序(如计数/基数排序),也不反映实际运行中的常数因子和内存开销。基于 CLRS《算法导论》标准分析模型。
3 种主流语言 · 复制即用
import random
# 冒泡排序:相邻元素比较交换,每轮将最大值冒到末尾
def bubble_sort(arr):
n = len(arr)
for i in range(n):
swapped = False
for j in range(n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break # 已有序,提前结束
return arr
# 测试
data = [64, 34, 25, 12, 22, 11, 90]
print(bubble_sort(data)) # [11, 12, 22, 25, 34, 64, 90]package main
import "fmt"
// 快速排序:选基准,分区递归
func quickSort(arr []int) []int {
if len(arr) < 2 {
return arr
}
pivot := arr[0]
var left, right []int
for _, v := range arr[1:] {
if v <= pivot {
left = append(left, v)
} else {
right = append(right, v)
}
}
result := append(quickSort(left), pivot)
result = append(result, quickSort(right)...)
return result
}
func main() {
data := []int{64, 34, 25, 12, 22, 11, 90}
fmt.Println(quickSort(data)) // [11 12 22 25 34 64 90]
}// 归并排序:分治合并
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
const result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result.push(left[i++]);
} else {
result.push(right[j++]);
}
}
return result.concat(left.slice(i)).concat(right.slice(j));
}
const data = [64, 34, 25, 12, 22, 11, 90];
console.log(mergeSort(data)); // [11, 12, 22, 25, 34, 64, 90]7 个高频疑问
「HTTP / 网络速查」下的其他工具