最近在leetcode上学习时,看到了很多coder使用算法动图来讲题解,非常清晰明了,着实是羡慕。于是在网上查了下是否有什么可视化工具,能以动画的形式展现算法的执行过程。于是发现了这个东西:algorithm-visualize。这对于我这种特别不会画图、不会图像化记录解题思路的人,是个小小的惊喜。
我们可以去来体验一下:体验地址。最左边是算法目录,包括排序、二叉树、图、动规等很多经典的算法。中间是算法动画演示区域,右边是代码coding区域。我们如果对哪个算法感兴趣,可以直接点击【运行Play】按钮就可以了。
但是啊,这些算法都是已经结合可视化程序写好的,我想把它换成自己想运行的算法,就得了解它可视化的部分如何实现,但是在网上却几乎搜索不到对应的教程,就捣鼓学习了一下。
冒泡排序原代码如下:
function bubbleSort(arr) {
let len = arr.length;
let done = true; // 我们加一个标志位,如果某次循环完后,没有任何两数进行交换,就将标志位 设置为 true,表示排序完成,这样我们就可以减少不必要的排序,提高性能
// 如果当前循环已经不需要排序了,则说明此时已经是排好序的数组了,不需要进入到下一轮循环
while (done) {
done = false;
// 以下为一轮完整的冒泡,可以完成第(len-i-1)个数的排序
for (let i = 1; i < len; i++) {
// 如果当前值大于后一个值,就进行交换
if (arr[i - 1] > arr[i]) {
const temp = arr[i - 1];
arr[i - 1] = arr[i];
arr[i] = temp;
done = true; // 存在交换,就置为false
}
}
len--;
}
return arr;
}加入可视化程序如下:

详细代码如下:
// 引入algorithm-visualizer包
const { Tracer, Array1DTracer, ChartTracer, LogTracer, Randomize, Layout, VerticalLayout } = require('algorithm-visualizer');
const chart = new ChartTracer(); // 实例化ChartTracer
const tracer = new Array1DTracer(); // 实例化Array1DTracer
const logger = new LogTracer(); // 实例化LogTracer
Layout.setRoot(new VerticalLayout([chart, tracer, logger])); // 以垂直方式排列统计图、一维数组、输出日志
const arr = Randomize.Array1D({ N: 10 }); // 随机初始化长度为15的一维数组
tracer.set(arr); // 初始化数组
tracer.chart(chart); // 以该数组生成柱状图
Tracer.delay(); // 暂停一下
// 打印信息,在这里打印的是原始数组
logger.println(`原始数组 = [${arr.join(', ')}]`);
function bubbleSort(arr) {
let len = arr.length;
let done = true; // 我们加一个标志位,如果某次循环完后,没有任何两数进行交换,就将标志位 设置为 true,表示排序完成,这样我们就可以减少不必要的排序,提高性能
// 如果当前循环已经不需要排序了,则说明此时已经是排好序的数组了,不需要进入到下一轮循环
while (done) {
done = false;
tracer.select(len - 1); // 选择最后一个数字
Tracer.delay(); // 暂停一下
// 以下为一轮完整的冒泡,可以完成第(len-i-1)个数的排序
for (let i = 1; i < len; i++) {
tracer.select(i); // 选择当前i对应的值
Tracer.delay(); // 暂停一下
// 如果当前值大于后一个值,就进行交换
if (arr[i - 1] > arr[i]) {
const temp = arr[i - 1];
arr[i - 1] = arr[i];
arr[i] = temp;
done = true; // 存在交换,就置为false
tracer.patch(i - 1, arr[i - 1]); // 显示i-1位置的值已经变化为了arr[i-1]值
tracer.patch(i, arr[i]); // 显示i位置的值已经变化为了arr[i]值
Tracer.delay(); // 暂停一下
tracer.depatch(i - 1); // 取消显示i-1位置值变化
tracer.depatch(i); // 取消显示i位置值变化
}
tracer.deselect(i); // 取消选择i位置的值
}
len--;
}
tracer.deselect(len - 1); // 取消选择最后一个数字
// 打印信息,在这里打印的是排好序后的数组
logger.println(`排序完数组 = [${arr.join(', ')}]`);
return arr;
}
bubbleSort(arr);以上代码的可视化运行效果如下:

二分查找原代码如下:
// 在数组arr找出terget的位置
function BinarySearch(arr, target) {
let start = 0;
let end = arr.length - 1;
let midNum;
while (start <= end) {
const midIdx = Math.floor((start + end) / 2);
midNum = arr[midIdx];
if (midNum < target) {
start = midIdx + 1;
} else if (midNum > target) {
end = midIdx - 1;
} else {
return midIdx;
}
}
return -1;
}加入可视化程序如下:

详细代码如下:
const { Tracer, Array1DTracer, ChartTracer, LogTracer, Randomize, Layout, VerticalLayout } = require('algorithm-visualizer');
const chart = new ChartTracer();
const tracer = new Array1DTracer();
const logger = new LogTracer();
Layout.setRoot(new VerticalLayout([chart, tracer, logger]));
// 生成随机数组,这里的arr是由0-50之间的数字组成的有序数组
const arr = Randomize.Array1D({ N: 15, value: () => Randomize.Integer({ min: 0, max: 50 }), sorted: true });
// 随机生成一个值
const target = arr[Randomize.Integer({ min: 0, max: arr.length - 1 })];
tracer.set(arr);
tracer.chart(chart);
Tracer.delay();
// 在数组arr找出terget的位置
function BinarySearch(arr, target) {
let start = 0;
let end = arr.length - 1;
let midNum;
while (start <= end) {
const midIdx = Math.floor((start + end) / 2);
midNum = arr[midIdx];
tracer.select(start, end); // 选择区间start-end
Tracer.delay(); // 暂停一下
tracer.patch(midIdx); // 高亮中间位置的值
Tracer.delay(); // 暂停一下
tracer.depatch(midIdx); // 取消高亮中间位置的值
tracer.deselect(start, end); // 取消选择区间start-end
if (midNum < target) {
start = midIdx + 1;
} else if (midNum > target) {
end = midIdx - 1;
} else {
logger.println(`${target}找到在位置${midIdx}!`);
tracer.select(midIdx); // 选择中间位置的值
return midIdx;
}
}
logger.println(`${target}没有找到!`);
return -1;
}
BinarySearch(arr, target);二分查找的可视化运行效果如下:

Array1DTracer:将一维数组写入table中。
const tracer = new Array1DTracer(); // 实例化Array1DTracer
const arr = Randomize.Array1D({ len: 15 }); // 随机初始化长度为15的一维数组
// 1. 初始化数组
tracer.set(arr);
// 2. 重置数组
tracer.reset();
// 3. 暂停显示变化
tracer.delay();
// 4. 可视化表示x位置的值已经变为了v
tracer.patch(x, v);
// 5. 停止可视化表示x位置的值变化了
tracer.depatch(x);
// 6. 选择x位置(某个位置)、选择from到to位置(某个区间)
tracer.select(x);
tracer.select(from, to);
// 7. 取消选择
tracer.deselect(x);
tracer.deselect(from, to);
// 8. 同步数组到统计图
tracer.chart();ChartTracer:以柱状图的形式展示一维数组
const chart = new ChartTracer(); // 实例化ChartTracer
// 所有的用户都和上述Array1DTracer的一致
// 1. 初始化数组
chart.set(arr);
// 2. 重置数组
chart.reset();
// 3. 暂停显示变化
chart.delay();
// 4. 可视化表示x位置的值已经变为了v
// ......LogTracer:打印日志
const logger = new LogTracer(); // 实例化日志
// 1. 打印内容
logger.print('I am message.'); // 一行打印
logger.println('I am message.'); // 换行打印随机初始化方法:
// 1. 随机创建一维数组,N为数组长度,sorted为是否排好序
const arr = Randomize.Array1D({ N: 15, sorted: false });
// 2. 随机创建二维数组,N为第一维长度,M为第二维长度,sorted为是否排好序
const arr = Randomize.Array2D({ N: 3, M: 4, sorted: false });
// 3. 随机创建浮点数
const doubleNum = Randomize.Double(min: 1, max: 10);
// 4. 随机创建小数
const intNum = Randomize.Integer(min: 1, max: 10);
// 5. 随机创建字符串,length为长度,letters为组成字符串可选择的字母
const randomString = Randomize.String(length: 10, letters: 'abcdefg');这里列举的是js语言的用法,当然这个平台也支持Java、python语言,大家也可以动手试试,在自己的程序中加入可视化代码,在平台中可看到运行的动画效果。这样其实可以帮助到我们排查错误、做笔记、记录题解,毕竟可视化是最直接的让人理解算法思想的好方式。