📅  最后修改于: 2023-12-03 14:49:41.353000             🧑  作者: Mango
堆排序是一种高效的排序算法,它将待排序的元素构造成一个二叉堆,从而实现排序。该算法的核心思想是通过调整堆的结构来完成排序。
在 JavaScript 中,堆排序可以使用数组来实现。因为 JavaScript 的数组支持动态扩容,在排序过程中不用担心数组容量不足的问题。
本文将介绍如何使用 JavaScript 实现堆排序,并通过可视化展示算法执行的过程。
堆排序分为两个步骤:建堆和排序。建堆需要先将数组构造成一个二叉堆,排序则需要不断地取出堆顶元素并调整堆的结构。
function heapSort(arr) {
// 建堆
buildHeap(arr);
// 排序
for (let i = arr.length - 1; i > 0; i--) {
// 将堆顶元素与尾部元素交换
swap(arr, 0, i);
// 调整堆结构
heapify(arr, 0, i - 1);
}
return arr;
}
建堆需要从最后一个非叶子节点开始,依次对节点进行堆化操作。堆化操作包括两个步骤:先将当前节点与其左右子节点中的最大值交换,再将交换后的节点与其子节点中的最大值交换,直到达到堆底部为止。
function buildHeap(arr) {
const len = arr.length;
const lastNodeIndex = len >> 1;
for (let i = lastNodeIndex; i >= 0; i--) {
heapify(arr, i, len - 1);
}
}
堆化可以被认为是建堆中子过程的处理方式。它依赖于子树的状态,将一个子树调整为符合堆要求的状态。堆化操作包括两个步骤:先将当前节点与其左右子节点中的最大值交换,再将交换后的节点与其子节点中的最大值交换,直到达到堆底部为止。
function heapify(arr, nodeIndex, lastNodeIndex) {
let leftNodeIndex = 2 * nodeIndex + 1;
let rightNodeIndex = 2 * nodeIndex + 2;
let maxIndex = nodeIndex;
if (leftNodeIndex <= lastNodeIndex && arr[leftNodeIndex] > arr[maxIndex]) {
maxIndex = leftNodeIndex;
}
if (rightNodeIndex <= lastNodeIndex && arr[rightNodeIndex] > arr[maxIndex]) {
maxIndex = rightNodeIndex;
}
if (maxIndex !== nodeIndex) {
swap(arr, nodeIndex, maxIndex);
heapify(arr, maxIndex, lastNodeIndex);
}
}
swap 函数用于交换数组中两个元素的值。
function swap(arr, i, j) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
下面将使用 D3.js 来可视化堆排序的执行过程。
D3.js 是一个 JavaScript 库,它可以帮助我们快速地创建交互式的数据可视化,支持多种数据可视化类型,包括折线图、条形图、散点图等。
function visualize(arr) {
// 创建 SVG 元素
const svg = d3.select('#chart')
.append('svg')
.attr('width', '900')
.attr('height', '400');
// 计算元素位置
const margin = { top: 50, right: 50, bottom: 50, left: 50 };
const width = 900 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const xScale = d3.scaleLinear()
.domain([0, arr.length - 1])
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(arr)])
.range([height, 0]);
// 绘制矩形
svg.selectAll('rect')
.data(arr)
.enter()
.append('rect')
.attr('x', (d, i) => xScale(i))
.attr('y', (d) => yScale(d))
.attr('width', 20)
.attr('height', (d) => height - yScale(d))
.attr('fill', 'steelblue');
// 执行排序并可视化
heapSortVisualization(arr, svg, xScale, yScale);
}
function heapSortVisualization(arr, svg, xScale, yScale) {
const len = arr.length;
const lastNodeIndex = len >> 1;
let tempArr = [...arr];
// 建堆可视化
for (let i = lastNodeIndex; i >= 0; i--) {
setTimeout(() => {
heapifyVisualization(tempArr, svg, xScale, yScale, i, len - 1, true);
}, (i + 1) * 1000);
}
// 排序可视化
for (let i = len - 1; i > 0; i--) {
setTimeout(() => {
swap(tempArr, 0, i);
heapifyVisualization(tempArr, svg, xScale, yScale, 0, i - 1, false);
updateRect(tempArr, svg, xScale, yScale);
}, (len - i) * 1000);
}
}
function heapifyVisualization(arr, svg, xScale, yScale, nodeIndex, lastNodeIndex, isBuild) {
let leftNodeIndex = 2 * nodeIndex + 1;
let rightNodeIndex = 2 * nodeIndex + 2;
let maxIndex = nodeIndex;
if (leftNodeIndex <= lastNodeIndex && arr[leftNodeIndex] > arr[maxIndex]) {
maxIndex = leftNodeIndex;
}
if (rightNodeIndex <= lastNodeIndex && arr[rightNodeIndex] > arr[maxIndex]) {
maxIndex = rightNodeIndex;
}
if (maxIndex !== nodeIndex) {
swap(arr, nodeIndex, maxIndex);
if (isBuild) {
updateRect(arr, svg, xScale, yScale);
} else {
setTimeout(() => {
updateRect(arr, svg, xScale, yScale);
}, 500);
}
heapifyVisualization(arr, svg, xScale, yScale, maxIndex, lastNodeIndex, isBuild);
}
}
function updateRect(arr, svg, xScale, yScale) {
svg.selectAll('rect')
.data(arr)
.transition()
.duration(500)
.attr('y', (d) => yScale(d))
.attr('height', (d) => yScale(0) - yScale(d));
}
效果如下:
使用 JavaScript 实现堆排序是一个比较简单的过程。通过可视化可以更好地理解算法的执行过程。D3.js 是一个非常强大的数据可视化库,可以帮助我们快速地绘制各种类型的图表,并提供了一些趁手的交互功能。