【前端】可视化之快速排序

mac2024-12-22  22

在线体验

设计思路

1. 排序算法部分

快速排序的思想就是在序列中选择一个支点pivot, 让小于pivot的元素放在它的左边,不小于(说大于的话不准确)它的元素放在它的右边。

visualgo.net 的实现,维护三个序列,s1, s2, unknown

s1中存放小于的元素,s2中存放不小于支点的元素,unknown是未排序序列(具体的区分在动画中很清楚) function partition(a, i, j) { let m = i, p = a[i]; for (let k = i + 1; k <= j; k++) { if (a[k] < p) { m++; swap(a, m, k); } } swap(a, m, i); return m; } function quickSort(a, low, high) { if (low < high) { int m = partition(a, low, high); quickSort(a, low, m - 1); quickSort(a, m + 1, high); } }

2. 动画部分

排序的每一步操作存储在未来发生的回调函数执行队列中,全局变量 step 代表操作步数

假设每一步动画时延为1s, 那么在未来的0s, 1s, 2s, ..., step * 1s 将会在页面上看到具体的排序操作, 如果没有CSS的过渡效果,那么整个过程将会显得十分生硬。 例如我将第一个节点的位置与第3个节点的位置交换, 在 0s - 1s 有一段时间间隔,设置 transiton: all 1s 就能看到过渡效果。

完整代码

<script> let step = 0; let nodeList = document.getElementsByClassName('num'), reloadBtn = document.querySelector('.reload'), sortBtn = document.querySelector('.sort'); let initColor = 'rgb(173, 216, 230)', sortedColor = 'rgb(255, 165, 0)', pivoColor = 'rgb(255, 255, 0)', s1Color = 'rgb(60, 179, 113)', s2Color = 'rgb(153, 50, 204)', activeColor = 'rgb(220, 20, 60)'; let colorMap = { 'sorted': sortedColor, 's1': s1Color, 's2': s2Color, 'init': initColor, 'pivo': pivoColor, 'active': activeColor }; let showConsoleNode = document.querySelector('.showConsole'); let arr = [], timeout = 0.4, queue = []; function handleChange() { let selectOps = document.querySelector('#speed-control'); // console.log(selectOps) let selectIdx = selectOps.selectedIndex; let val = selectOps.options[selectIdx].value; timeout = Number(val); console.log(timeout) } function swapPosition(node1, node2) { [node1.style.left, node2.style.left] = [node2.style.left, node1.style.left]; } function setBgColor(nodeArr, color) { for (let i = 0; i < nodeArr.length; i++) { nodeArr[i].style.background = color; } } function swap(arr, i, j) { [arr[i], arr[j]] = [arr[j], arr[i]]; } function createFrameForChangeColor(arr, step, nodeType) { setTimeout(() => { console.log('select:' + arr[0].innerText); setBgColor(arr, colorMap[nodeType]); }, step * timeout * 1.6 * 1000); } function createFrameForSwapPos(node1, node2, step) { setTimeout(() => { console.log(`swap ${node1.innerText}, ${node2.innerText}`); swapPosition(node1, node2) }, step * timeout * 1.6 * 1000) } function resetColorForPartOfArr(arr, l, r, m, step) { setTimeout(() => { for (let i = l; i <= r; i++) { let node = nodeList[arr[i].nodeIndex]; if (i !== m) { setBgColor([node], initColor); } else { setBgColor([node], sortedColor); } } }, step * timeout * 1.6 * 1000); } function resetColorForAll(step) { setTimeout(() => { for (let i = 0; i < nodeList.length; i++) { let node = nodeList[arr[i].nodeIndex]; setBgColor([node], initColor); } }, step * timeout * 1.6 * 1000); } function partition(a, i, j) { let m = i, p = a[i]; createFrameForChangeColor([nodeList[a[i].nodeIndex]], step++, 'pivo'); for (let k = i + 1; k <= j; k++) { createFrameForChangeColor([nodeList[a[k].nodeIndex]], step++, 'active'); if (a[k].val < p.val) { m++; createFrameForChangeColor([nodeList[a[k].nodeIndex]], step++, 's1'); createFrameForSwapPos(nodeList[a[m].nodeIndex], nodeList[a[k].nodeIndex], step++); swap(a, m, k); } else { createFrameForChangeColor([nodeList[a[k].nodeIndex]], step++, 's2') } } createFrameForSwapPos(nodeList[a[m].nodeIndex], nodeList[a[i].nodeIndex], step++); swap(a, m, i); resetColorForPartOfArr(arr, i, j, m, step++); return m; } function quickSort(a, low, high) { if (low <= high) { let m = partition(a, low, high); quickSort(a, low, m - 1); quickSort(a, m + 1, high); } } function init() { let fragment = document.createDocumentFragment(); for (let i = 0; i < 10; i++) { let div = document.createElement('div'), num = parseInt(Math.random() * 10 + 1); arr.push({ val: num, nodeIndex: i, //这里保存的是节点在nodeList中的位置 }); div.className = 'num'; div.style.height = `${num * 20}px`; div.style.left = `${i * 50}px`; div.style.bottom = '0'; div.style.background = initColor; div.style.transition = `left ${timeout}s, background ${timeout * 0.6}s`; div.innerText = num; fragment.append(div); } document.querySelector('.animate-contanier').append(fragment); } function reload() { arr = []; for (let i = 0; i < 10; i++) { let div = nodeList[i], num = parseInt(Math.random() * 10 + 1); arr.push({ val: num, nodeIndex: i, }); div.style.left = `${i * 50}px`; div.style.bottom = '0'; div.style.height = `${num * 20}px`; div.innerText = num; } } init(); document.querySelector('.sort').addEventListener('click', function () { quickSort(arr, 0, 9, 0); resetColorForAll(step) }) </script>
最新回复(0)