学习算法时,你是否常常觉得概念抽象,难以在脑中构建出数据流动的具象画面?
- 看了几遍插入排序的代码,还是不理解为什么从后往前比较?
- 脑子里想象不出元素移动的过程,只能死记硬背?
- 网上找的动画演示太快或太慢,无法反复琢磨?
- 想深入理解算法,但纸上画图效率太低?
今天,我们将采用一种“动手实现”的进阶学习法——自己动手,使用 Remotion 这个基于 React 的视频创作框架,来制作一个算法可视化视频。通过编写代码来“生成”理解,这才是真正的深度学习。当我自己实现这个视频时,才恍然大悟:原来插入排序的“空位”和“元素后移”是这样的!
🌩️ 什么是插入排序?
在动手制作视频之前,我们先来快速回顾一下插入排序的原理。
📖 生活化理解
想象你在玩扑克牌,手里已经有一排排好序的牌:
[3♣] [5♦] [7♥] [9♠] ← 已排序的牌
这时候你摸到一张 4♠,要把它插到正确的位置:
第1步:和9♠比,4 < 9,把9往后移 → [3] [5] [7] [空] [9]
第2步:和7♥比,4 < 7,把7往后移 → [3] [5] [空] [7] [9]
第3步:和5♦比,4 < 5,把5往后移 → [3] [空] [5] [7] [9]
第4步:和3♣比,4 > 3,找到了!插在3后面 → [3] [4] [5] [7] [9]
这就是插入排序的核心思想:
- 将数组分为已排序和未排序两部分
- 每次从未排序部分取出第一个元素
- 在已排序部分中找到合适的位置插入
- 重复直到所有元素排序完成
💡 重点理解:
- 为什么要“从后往前”比较?因为我们边比较边移位
- “空位”是如何产生的?元素后移腾出的空间
- 如何找到插入位置?遇到第一个 ≤ key 的元素
📂 第一步:设计数据结构
准备环境:
npm init remotion
npm install
技术栈:React + TypeScript + Remotion
要展示插入排序的每一步,我们需要记录:
- 当前数组状态
- 正在比较的元素
- 已排序的部分
- 待插入的元素及其位置
- 空位标记
interface SortStep {
array: number[]; // 当前数组状态
comparing: number[]; // 正在比较的元素索引
sorted: number[]; // 已排序元素的索引
stepType: 'comparing' | 'moving' | 'inserting'; // 步骤类型
keyValue?: number; // 待插入元素的值
keyIndex?: number; // 待插入元素的原位置
emptySlot?: number; // 移动后空出来的位置
subtitle: string; // 字幕说明
}
💻 第二步:生成排序步骤
这是最核心的部分,需要记录每一步操作:
export const generateInsertionSortSteps = (arr: number[]): SortStep[] => {
const steps: SortStep[] = [];
const array = [...arr];
const n = array.length;
const sorted: number[] = [0]; // 第一个元素默认已排序
for (let i = 1; i < n; i++) {
const key = array[i]; // 取出待插入元素
let j = i - 1;
let currentEmptySlot = i; // 初始空位就是key的位置
// 在已排序部分从后向前查找插入位置
while (j >= 0 && array[j] > key) {
// 记录比较步骤
steps.push({
array: [...array],
comparing: [j],
stepType: 'comparing',
keyValue: key,
emptySlot: currentEmptySlot,
subtitle: `比较 ${array[j]} 和 ${key}`
});
// 移动元素,更新空位
array[j + 1] = array[j];
currentEmptySlot = j;
// 记录移动步骤
steps.push({
array: [...array],
comparing: [j, j + 1],
stepType: 'moving',
keyValue: key,
emptySlot: currentEmptySlot,
subtitle: `${array[j]} 后移一位`
});
j--;
}
// 插入元素
array[j + 1] = key;
steps.push({
array: [...array],
stepType: 'inserting',
keyValue: key,
subtitle: `插入 ${key} 到位置 ${j + 1}`
});
}
return steps;
};
🔑 第三步:可视化渲染
Remotion 的核心是React 组件,每一帧都是一个 React 渲染结果:
export const InsertionSort: React.FC = () => {
const frame = useCurrentFrame(); // 获取当前帧
const steps = generateInsertionSortSteps([64, 34, 25, 12, 22, 11, 90]);
// 计算当前步骤
const stepIndex = Math.floor(frame / FRAMES_PER_STEP);
const currentStep = steps[stepIndex];
return (
<AbsoluteFill style={{ backgroundColor: '#1a1a2e' }}>
{/* 标题 */}
<div>插入排序 Insertion Sort</div>
{/* 已排序/未排序区域标识 */}
<div>✓ 已排序: 前 {boundaryIndex + 1} 个</div>
<div>未排序: 后 {n - boundaryIndex - 1} 个</div>
{/* 待插入元素卡片 */}
<div>待插入元素: {keyValue}</div>
{/* 数组条形图 */}
{currentStep.array.map((value, index) => {
// 空位特殊处理
if (isEmptySlot) {
return (
<div>
<div style={{ border: '2px dashed #8b949e' }}>
空
</div>
{/* 悬空的待插入元素 */}
<div style={{ color: '#ffd700' }}>
{keyValue} ↓
</div>
</div>
);
}
return <div style={{ backgroundColor: getColor(index) }} />;
})}
{/* 伪代码面板 */}
<div>
{pseudocode.map((line, i) => (
<div style={{
backgroundColor: i === codeLine ? '#2d4a22' : 'transparent',
borderLeft: i === codeLine ? '3px solid #4ecca3' : 'none'
}}>
{line}
</div>
))}
</div>
</AbsoluteFill>
);
};
🎨 第四步:颜色编码系统
为了让演示更直观,我们设计了清晰的视觉系统:
| 颜色 |
状态 |
说明 |
| 🟣 紫色 |
未排序 |
尚未处理的元素 |
| 🟢 绿色 |
已排序 |
已插入到正确位置 |
| 🟡 金色 |
待插入 |
从数组中取出的key元素 |
| 🔵 蓝色 |
比较中 |
正在比较的元素 |
| 🔴 红色 |
移动中 |
正在向后移动的元素 |
空位特殊显示:
- 虚线边框 + 半透明
- 标注“空”字
- 上方显示金色的待插入元素
⚡ 第五步:导出视频
预览视频:
npm start
访问 http://localhost:3000,实时预览效果
导出 MP4:
npm run build
输出高清 MP4 视频:
- 分辨率:1920×1080
- 帧率:30fps
- 每步停留:30帧(约1秒)
💡 核心技术亮点
1. 空位可视化
传统教学无法展示“空位”,学生很难理解元素移动过程。我们的方案:
移动前:[34] [64] [25] [12] [22]
↑
待插入
移动过程:
[34] [空] [64] [12] [22]
↑
[25] 金色悬空
[空] [34] [64] [12] [22]
↑
[25] 金色悬空
学生能清晰看到:
- 哪个位置空出来了
- 待插入元素在哪里悬着
- 元素如何向后移动
2. 已排序/未排序分界线
在排序过程中,用金色竖线标记分界:
[25] [34] [64] | [12] [22]
已排序部分 ↑ 未排序部分
分界线
3. 伪代码实时高亮
右侧代码面板同步显示:
3 | key = array[i] ← 当前执行(绿色高亮)
4 | j = i - 1
5 | while j >= 0 and array[j] > key:
学生能直观看到:代码在执行哪一行,数组在发生什么变化
4. 布局稳定性
所有信息区域固定高度,避免内容闪烁:
- 已排序/未排序标识:固定50px
- 待插入元素卡片:固定70px
- 插入位置提示:固定50px
📊 传统教学 vs 可视化教学
| 维度 |
传统教学 |
Remotion 可视化 |
| 直观性 |
代码+口述 |
动态条形图+颜色编码 |
| 参与感 |
被动接收 |
主动观察每一步 |
| 理解难度 |
抽象难懂 |
具象易懂 |
| 修改成本 |
改PPT需半天 |
改代码几分钟 |
| 可复用性 |
低 |
高(代码复用) |
| 制作成本 |
低(但效果差) |
低(代码即视频) |
✅ 效果展示
最终生成的视频具有以下特点:
视觉层面:
- ✨ 条形图高度对应数值大小
- 🎨 颜色清晰区分不同状态
- 📍 空位虚线框 + 悬空元素
- 🎯 分界线标记已排序部分
教学层面:
- 📝 字幕实时解释操作
- 💻 伪代码同步高亮
- 🔍 步骤可暂停、可回放
- 📊 复杂度对比清晰
🚀 进阶玩法推荐
1. 多种排序算法对比
已经实现:
可以继续添加:
让学生对比不同算法的执行过程。
2. 性能对比可视化
添加计时器和交换次数统计:
冒泡排序:交换 45 次,耗时 2.3s
插入排序:交换 12 次,耗时 1.1s
3. 自定义数据
允许学生输入自己的数组:
const initialArray = [/* 学生自定义 */];
4. 速度调节
调整 FRAMES_PER_STEP 控制演示速度:
export const FRAMES_PER_STEP = 30; // 30帧≈1秒
export const FRAMES_PER_STEP = 15; // 15帧≈0.5秒(加速)
💪 我的实践心得
在实现这个视频的过程中,我遇到了几个关键问题,解决后才真正理解了插入排序:
问题1:如何展示“空位”?
最初的错误想法:直接显示移动后的数组
问题:看不出来元素是怎么移动的,也看不出“空位”
最终方案:
// 标记空位
emptySlot: currentEmptySlot
// 渲染时特殊处理
if (isEmptySlot) {
// 只显示虚线框 + “空”字
// 上方悬空显示待插入元素
}
收获:理解了为什么插入排序是“从后往前比较”——这样可以边比较边腾出空间!
问题2:如何追踪待插入元素的位置?
困惑:元素不断移动,key 应该显示在哪?
理解过程:
- 初始:key 在位置 i
- 每移动一次:空位向前移动一位
- 最终:插入到正确位置
收获:这才理解了算法中 j 变量的作用——追踪空位位置!
问题3:布局为什么会跳动?
问题:信息框时有时无,下面的柱状图上下跳动
解决方案:所有区域固定高度
// 已排序/未排序标识:固定50px高度
<div style={{ height: 50 }}>
{显示内容 或 占位符}
</div>
收获:学会了 UI 稳定性设计,也加深了对 React 渲染的理解。
🎯 学完后的收获
通过自己动手实现这个视频,我获得了远超看教程的理解深度:
1. 算法理解更深刻
以前:知道插入排序是“取元素插入到已排序部分”
现在:理解了每一步的细节:
- 空位是如何产生的
- 为什么从后往前比较
- 边界条件如何处理(j < 0)
2. 编程能力提升
TypeScript 类型设计:
interface SortStep {
array: number[];
stepType: 'comparing' | 'moving' | 'inserting';
emptySlot?: number; // 这个字段让我理解了“空位”的概念
}
React 状态管理:理解了如何用 React 管理复杂的可视化状态
3. 技术视野拓展
Remotion 应用场景:
- 算法可视化教学视频
- 产品演示视频
- 数据可视化动画
- 技术分享 PPT 的动态版本
核心价值:代码即视频,修改方便,可复用!
📦 项目结构
insertion-sort-video/
├── src/
│ ├── index.tsx # 入口,注册组件
│ ├── BubbleSort.tsx # 冒泡排序
│ └── InsertionSort.tsx # 插入排序 ⭐
├── out/
│ └── insertion-sort.mp4 # 导出视频
├── package.json
└── README.md
🎯 总结
用 Remotion 制作算法可视化,我最大的感悟是:
“看懂了是别人的,做出来才是自己的”
- 动手实现:比看10遍教程都深刻
- 可视化思维:强迫你思考每一步的细节
- 代码即视频:修改方便,还能复用到其他算法
- 技术栈延伸:顺便学会了 React + TypeScript + Remotion
最关键的是:自己做出的视频,可以反复看,可以分享给其他人,成就感爆棚!
赶紧去试试吧!这种将理解转化为可运行、可观看的开源项目的过程,本身就是一种极佳的深度学习路径。如果你在实现过程中遇到问题,或者有更好的想法,欢迎在 云栈社区 的对应板块交流探讨。
你还想看哪种算法的可视化?快排?归并排序?堆排序?不妨在评论区告诉我们。