太震撼了,我把七大JS排序算法做成了可视化

  • 元素对应的索引index * 10 -> 角度θ(为什么要乘10呢,因为要凑够360°嘛)

  • 元素对应的值arr[index] -> 极径ρ

那么我们可以的到这37个点,自然就可以实现打散的效果

(25 -> θ = 00°,ρ = 25) (8 -> θ = 10°,ρ = 8) (32 -> θ = 20°,ρ = 32) (1 -> θ = 30°,ρ = 1)

(19 -> θ = 40°,ρ = 19) (14 -> θ = 50°,ρ = 14) (0 -> θ = 60°,ρ = 0) (29 -> θ = 70°,ρ = 29)

(17 -> θ = 80°,ρ = 17) (6 -> θ = 90°,ρ = 6) (7 -> θ = 100°,ρ = 7) (26 -> θ = 110°,ρ = 26)

(3 -> θ = 120°,ρ = 3) (30 -> θ = 130°,ρ = 30) (31 -> θ = 140°,ρ = 31) (16 -> θ = 150°,ρ = 16)

(28 -> θ = 160°,ρ = 28) (15 -> θ = 170°,ρ = 15) (24 -> θ = 180°,ρ = 24) (10 -> θ = 190°,ρ = 10)

(21 -> θ = 200°,ρ = 21) (2 -> θ = 210°,ρ = 2) (9 -> θ = 220°,ρ = 9) (4 -> θ = 230°,ρ = 4)

(35 -> θ = 240°,ρ = 35) (5 -> θ = 250°,ρ = 5) (36 -> θ = 260°,ρ = 36) (33 -> θ = 270°,ρ = 33)

(11 -> θ = 280°,ρ = 11) (27 -> θ = 290°,ρ = 27) (34 -> θ = 300°,ρ = 34) (22 -> θ = 310°,ρ = 22)

(13 -> θ = 320°,ρ = 13) (18 -> θ = 330°,ρ = 18) (23 -> θ = 340°,ρ = 23) (12 -> θ = 350°,ρ = 12)

(20 -> θ = 360°,ρ = 20)

截屏2021-09-05 下午7.32.17.png

实现效果

综上所述,咱们想实现效果,也就有了思路

  • 1、先生成一个乱序数组

  • 2、用canvas画布画出此乱序数组所有元素对应的极坐标对应的点

  • 3、对乱序数组进行排序

  • 4、排序过程中不断清空画布,并重画数组所有元素对应的极坐标对应的点

  • 5、直到排序完成,终止画布操作

截屏2021-09-05 下午7.41.54.png

开搞!!!


咱们,做事情一定要有条有理才行,还记得上面说的步骤吗?

  • 1、先生成一个乱序数组

  • 2、用canvas画布画出此乱序数组所有元素对应的极坐标对应的点

  • 3、对乱序数组进行排序

  • 4、排序过程中不断清空画布,并重画数组所有元素对应的极坐标对应的点

  • 5、直到排序完成,终止画布操作

咱们就按照这个步骤,来一步一步实现效果,兄弟们,冲啊!!!

生成乱序数组

咱们上面举的例子是37个元素,但是37个肯定是太少了,咱们搞多点吧,我搞了这么一个数组nums:我先生成一个0 - 179的有序数组,然后打乱,并塞进数组nums中,此操作我执行4次。为什么是0 - 179,因为0 - 179刚好有180个数字

身位一个程序员,我肯定不可能自己手打这么多元素的啦。。来。。上代码

let nums = []

for (let i = 0; i < 4; i++) {

// 生成一个 0 - 179的有序数组

const arr = […Array(180).keys()] // Array.keys()可以学一下,很有用

const res = []

while (arr.length) {

// 打乱

const randomIndex = Math.random() * arr.length - 1

res.push(arr.splice(randomIndex, 1)[0])

}

nums = […nums, …res]

}

经过上面操作,也就是我的nums中拥有4 * 180 = 720个元素,nums中的元素都是0 - 179范围内的

canvas画乱序数组

画canvas之前,肯定要现在html页面上,编写一个canvas的节点,这里我宽度设置1000,高度也是1000,并且背景颜色是黑色

上面看到了,极点(原点)是在坐标正中间的,但是canvas的初始原点是在画布的左上角,我们需要把canvas的原点移动到画布的正中间,那正中间的坐标是多少呢?还记得咱们宽高都是1000吗?那画布中心点坐标不就是(500, 500),咱们可以使用canvas的ctx.translate(500, 500)来移动中心点位置。因为咱们画的点都是白色的,所以咱们顺便把ctx.fillStyle设置为white

有一点注意了哈,canvas里的Y轴是自上向下的,与常规的Y轴的相反的。

截屏2021-09-05 下午8.55.39.png

const canvas = document.getElementById(‘canvas’)

const ctx = canvas.getContext(‘2d’)

ctx.fillStyle = ‘white’ // 设置画画的颜色

ctx.translate(500, 500) // 移动中心点到(500, 500)

那到底该怎么画点呢?按照之前的,其实光计算出角度θ极径ρ是不够的,因为canvas画板不认这两个东西啊。。那canvas认啥呢,他只认(x, y),所以咱们只要通过角度θ极径ρ去算出(x, y),就好了,还记得前面极坐标的公式吗

  • x = ρ * cosθ,因为x / ρ = cosθ

  • y = ρ * sinθ,因为y / ρ = sinθ

由于咱们是要铺散点是要铺出一个圆形来,那么一个圆形的角度是0° - 360°,但是我们不要360°,咱们只要0° - 359°,因为0°和360°是同一个直线。咱们一个直线上有一个度数就够了。所以咱们要求出0° - 359°每个角度所对应的cosθ和sinθ(这里咱们只算整数角度,不算小数角度)

const CosandSin = []

for (let i = 0; i < 360; i++) {

const jiaodu = i / 180 * Math.PI

CosandSin.push({ cos: Math.cos(jiaodu), sin: Math.sin(jiaodu) })

}

这时候又有新问题了,咱们一个圆上的整数角度只有0° - 359°360个整数角,但是nums中有720个元素啊,那怎么分配画布呢?很简单啊,一个角度上画2个元素,那不就刚好 2 * 360 = 720

行,咱们废话不多说,开始画初始散点吧。咱们也知道咱们需要画720个点,对于这种多个相同的东西,咱们要多多使用面向对象这种编程思想

// 单个长方形构造函数

function Rect(x, y, width, height) {

this.x = x // 坐标x

this.y = y // 坐标y

this.width = width // 长方形的宽

this.height = height // 长方形的高

}

// 单个长方形的渲染函数

Rect.prototype.draw = function () {

ctx.beginPath() // 开始画一个

ctx.fillRect(this.x, this.y, this.width, this.height) // 画一个

ctx.closePath() // 结束画一个

}

const CosandSin = []

for (let i = 0; i < 360; i++) {

const jiaodu = i / 180 * Math.PI

CosandSin.push({ cos: Math.cos(jiaodu), sin: Math.sin(jiaodu) })

}

function drawAll(arr) {

const rects = [] // 用来存储720个长方形

for (let i = 0; i < arr.length; i++) {

const num = arr[i]

const { cos, sin } = CosandSin[Math.floor(i / 2)] // 一个角画两个

const x = num * cos // x = ρ * cosθ

const y = num * sin // y = ρ * sinθ

rects.push(new Rect(x, y, 5, 3)) // 收集所有长方形

}

rects.forEach(rect => rect.draw()) // 遍历渲染

}

drawAll(nums) // 执行渲染函数

来页面中看看效果吧。此时就完成了初始的散点渲染

截屏2021-09-05 下午6.05.45.png

边排序边重画

其实很简单,就是排序一次,就清空画布,然后重新执行上面的渲染函数drawAll就行了。由于性能原因,我先把drawAll封装成一个Promise函数

function drawAll(arr) {

return new Promise((resolve) => {

setTimeout(() => {

ctx.clearRect(-500, -500, 1000, 1000) // 清空画布

const rects = [] // 用来存储720个长方形

for (let i = 0; i < arr.length; i++) {

const num = arr[i]

const { cos, sin } = CosandSin[Math.floor(i / 2)] // 一个角画两个

const x = num * cos // x = ρ * cosθ

const y = num * sin // y = ρ * sinθ

rects.push(new Rect(x, y, 5, 3)) // 收集所有长方形

}

rects.forEach(rect => rect.draw()) // 遍历渲染

resolve(‘draw success’)

}, 10)

})

}

然后咱们拿一个排序算法例子来讲一讲,就拿个冒泡排序来讲吧

async function bubbleSort(arr) {

var len = arr.length;

for (var i = 0; i < len; i++) {

for (var j = 0; j < len - 1 - i; j++) {

if (arr[j] > arr[j + 1]) { //相邻元素两两对比

var temp = arr[j + 1]; //元素交换

arr[j + 1] = arr[j];

arr[j] = temp;

}

}

await drawAll(arr) // 一边排序一边重新画

}

return arr;

}

然后在页面里放一个按钮,用来执行开始排序

开始排序

document.getElementById(‘btn’).onclick = function () {

bubbleSort(nums)

}

效果如下,是不是很开心哈哈哈!!!

冒泡排序gift.gif

完整代码

这是完整代码

开始排序

const canvas = document.getElementById(‘canvas’)

const ctx = canvas.getContext(‘2d’)

ctx.fillStyle = ‘white’ // 设置画画的颜色

ctx.translate(500, 500) // 移动中心点到(500, 500)

let nums = []

for (let i = 0; i < 4; i++) {

// 生成一个 0 - 180的有序数组

const arr = […Array(180).keys()]

const res = []

while (arr.length) {

// 打乱

const randomIndex = Math.random() * arr.length - 1

res.push(arr.splice(randomIndex, 1)[0])

}

nums = […nums, …res]

}

// 单个长方形构造函数

function Rect(x, y, width, height) {

this.x = x // 坐标x

this.y = y // 坐标y

this.width = width // 长方形的宽

this.height = height // 长方形的高

}

// 单个长方形的渲染函数

Rect.prototype.draw = function () {

ctx.beginPath() // 开始画一个

ctx.fillRect(this.x, this.y, this.width, this.height) // 画一个

ctx.closePath() // 结束画一个

}

const CosandSin = []

for (let i = 0; i < 360; i++) {

const jiaodu = i / 180 * Math.PI

CosandSin.push({ cos: Math.cos(jiaodu), sin: Math.sin(jiaodu) })

}

function drawAll(arr) {

return new Promise((resolve) => {

setTimeout(() => {

ctx.clearRect(-500, -500, 1000, 1000) // 清空画布

const rects = [] // 用来存储720个长方形

for (let i = 0; i < arr.length; i++) {

const num = arr[i]

const { cos, sin } = CosandSin[Math.floor(i / 2)] // 一个角画两个

const x = num * cos // x = ρ * cosθ

const y = num * sin // y = ρ * sinθ

rects.push(new Rect(x, y, 5, 3)) // 收集所有长方形

}

rects.forEach(rect => rect.draw()) // 遍历渲染

resolve(‘draw success’)

}, 10)

})

}

drawAll(nums) // 执行渲染函数

async function bubbleSort(arr) {

var len = arr.length;

for (var i = 0; i < len; i++) {

for (var j = 0; j < len - 1 - i; j++) {

if (arr[j] > arr[j + 1]) { //相邻元素两两对比

var temp = arr[j + 1]; //元素交换

arr[j + 1] = arr[j];

arr[j] = temp;

}

}

await drawAll(arr) // 一边排序一边重新画

}

return arr;

}

document.getElementById(‘btn’).onclick = function () {

bubbleSort(nums) // 点击执行

}

正片开始!!!


首先说明,哈哈

  • 我是算法渣渣

  • 每种算法排序,动画都不一样

  • drawAll放在不同地方也可能有不同效果

冒泡排序

async function bubbleSort(arr) {

var len = arr.length;

for (var i = 0; i < len; i++) {

for (var j = 0; j < len - 1 - i; j++) {

if (arr[j] > arr[j + 1]) { //相邻元素两两对比

var temp = arr[j + 1]; //元素交换

arr[j + 1] = arr[j];

arr[j] = temp;

}

}

await drawAll(arr) // 一边排序一边重新画

}

return arr;

}

document.getElementById(‘btn’).onclick = function () {

bubbleSort(nums) // 点击执行

}

选择排序

async function selectionSort(arr) {

var len = arr.length;

var minIndex, temp;

for (var i = 0
; i < len - 1; i++) {

minIndex = i;

for (var j = i + 1; j < len; j++) {

if (arr[j] < arr[minIndex]) { //寻找最小的数

minIndex = j; //将最小数的索引保存

}

}

temp = arr[i];

arr[i] = arr[minIndex];

arr[minIndex] = temp;

await drawAll(arr)

}

return arr;

}

document.getElementById(‘btn’).onclick = function () {

selectionSort(nums)

}

插入排序

async function insertionSort(arr) {

if (Object.prototype.toString.call(arr).slice(8, -1) === ‘Array’) {

for (var i = 1; i < arr.length; i++) {

var key = arr[i];

var j = i - 1;

while (j >= 0 && arr[j] > key) {

arr[j + 1] = arr[j];

j–;

}

arr[j + 1] = key;

await drawAll(arr)

}

return arr;

} else {

return ‘arr is not an Array!’;

}

}

document.getElementById(‘btn’).onclick = function () {

insertionSort(nums)

}

堆排序

async function heapSort(array) {

if (Object.prototype.toString.call(array).slice(8, -1) === ‘Array’) {

//建堆

var heapSize = array.length, temp;

for (var i = Math.floor(heapSize / 2) - 1; i >= 0; i–) {

heapify(array, i, heapSize);

await drawAll(array)

}

//堆排序

for (var j = heapSize - 1; j >= 1; j–) {

temp = array[0];

array[0] = array[j];

array[j] = temp;

heapify(array, 0, --heapSize);

await drawAll(array)

}

return array;

} else {

return ‘array is not an Array!’;

}

}

function heapify(arr, x, len) {

if (Object.prototype.toString.call(arr).slice(8, -1) === ‘Array’ && typeof x === ‘number’) {

var l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;

if (l < len && arr[l] > arr[largest]) {

largest = l;

}

if (r < len && arr[r] > arr[largest]) {

largest = r;

}

if (largest != x) {

temp = arr[x];

arr[x] = arr[largest];

arr[largest] = temp;

heapify(arr, largest, len);

}

} else {

return ‘arr is not an Array or x is not a number!’;

}

}

document.getElementById(‘btn’).onclick = function () {

heapSort(nums)

}

快速排序

async function quickSort(array, left, right) {

drawAll(nums)

if (Object.prototype.toString.call(array).slice(8, -1) === ‘Array’ && typeof left === ‘number’ && typeof right === ‘number’) {

if (left < right) {

var x = array[right], i = left - 1, temp;

for (var j = left; j <= right; j++) {

if (array[j] <= x) {

i++;

temp = array[i];

array[i] = array[j];

array[j] = temp;

}

}

await drawAll(nums)

await quickSort(array, left, i - 1);

await quickSort(array, i + 1, right);

await drawAll(nums)

}

return array;

} else {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值