进阶3-4 小时·算法可视化

排序算法可视化器

实现 8 种经典排序算法的可视化对比,支持自定义数据、速度控制、步骤回放

algorithmssortingvisualizationcomparisonanimation

排序算法可视化器 - 实战案例

通过交互式动画直观理解8种经典排序算法的工作原理、时间复杂度差异和实际执行效率

你将学到

  1. 算法可视化实现 - 将抽象的算法逻辑转化为直观的动画演示
  2. 异步编程与动画控制 - 使用async/await控制算法执行节奏
  3. 用户交互设计 - 实现复杂的控制面板和实时反馈系统
  4. 性能优化技巧 - 在大量DOM操作中保持流畅的动画效果
  5. 状态管理 - 管理算法执行、暂停、回放等多状态交互

前置知识

  • JavaScript基础语法与异步编程
  • HTML5 Canvas或DOM操作基础
  • 基本的排序算法原理
  • 事件处理与用户交互设计

相关课程参考:数组基础时间复杂度与空间复杂度递归基础

架构设计

┌─────────────────────────────────────────────────────┐
│                   排序算法可视化器                    │
├─────────────────────────────────────────────────────┤
│  控制面板  │  可视化区域  │  算法信息面板  │
│  - 算法选择 │  - 数组可视化 │  - 算法描述    │
│  - 速度控制 │  - 比较动画   │  - 复杂度分析  │
│  - 数组操作 │  - 交换动画   │  - 执行统计    │
└─────────────────────────────────────────────────────┘

数据流:
[用户操作] → [控制逻辑] → [排序算法] → [可视化引擎] → [用户界面]
     ↑                            ↓
     └───────────[状态管理]────────┘

实现步骤

步骤1:项目结构搭建

首先创建基础的HTML结构和CSS样式:

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>排序算法可视化器</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="app-container">
        <!-- 控制面板 -->
        <div class="control-panel">
            <h1>排序算法可视化器</h1>
            
            <div class="control-group">
                <label for="algorithm">选择算法:</label>
                <select id="algorithm">
                    <option value="bubble">冒泡排序</option>
                    <option value="selection">选择排序</option>
                    <option value="insertion">插入排序</option>
                    <option value="shell">希尔排序</option>
                    <option value="merge">归并排序</option>
                    <option value="quick">快速排序</option>
                    <option value="heap">堆排序</option>
                    <option value="counting">计数排序</option>
                </select>
            </div>
            
            <div class="control-group">
                <label for="speed">动画速度:</label>
                <input type="range" id="speed" min="1" max="100" value="50">
                <span id="speed-value">50</span>
            </div>
            
            <div class="control-group">
                <label for="array-size">数组大小:</label>
                <input type="range" id="array-size" min="5" max="100" value="20">
                <span id="array-size-value">20</span>
            </div>
            
            <div class="button-group">
                <button id="generate">生成随机数组</button>
                <button id="start">开始排序</button>
                <button id="pause" disabled>暂停</button>
                <button id="resume" disabled>继续</button>
                <button id="reset">重置</button>
            </div>
            
            <div class="stats-container">
                <div class="stat">
                    <span class="stat-label">比较次数:</span>
                    <span id="comparisons" class="stat-value">0</span>
                </div>
                <div class="stat">
                    <span class="stat-label">交换次数:</span>
                    <span id="swaps" class="stat-value">0</span>
                </div>
                <div class="stat">
                    <span class="stat-label">当前状态:</span>
                    <span id="status" class="stat-value">就绪</span>
                </div>
            </div>
        </div>
        
        <!-- 可视化区域 -->
        <div class="visualization-container">
            <div class="array-container" id="array-container">
                <!-- 数组柱状图将通过JavaScript动态生成 -->
            </div>
            
            <!-- 算法说明 -->
            <div class="algorithm-info" id="algorithm-info">
                <h3 id="algorithm-name">冒泡排序</h3>
                <p id="algorithm-description">通过重复遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。</p>
                <div class="complexity">
                    <div>时间复杂度: O(n²)</div>
                    <div>空间复杂度: O(1)</div>
                </div>
            </div>
        </div>
    </div>

    <script src="sorters.js"></script>
    <script src="visualizer.js"></script>
    <script src="app.js"></script>
</body>
</html>

步骤2:CSS样式设计

/* styles.css */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    padding: 20px;
}

.app-container {
    max-width: 1200px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 300px 1fr;
    gap: 20px;
}

.control-panel {
    background: rgba(255, 255, 255, 0.95);
    border-radius: 12px;
    padding: 20px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

.control-panel h1 {
    color: #333;
    margin-bottom: 20px;
    text-align: center;
    font-size: 1.4em;
}

.control-group {
    margin-bottom: 15px;
}

.control-group label {
    display: block;
    margin-bottom: 5px;
    font-weight: 600;
    color: #555;
}

.control-group select,
.control-group input[type="range"] {
    width: 100%;
    padding: 8px;
    border: 1px solid #ddd;
    border-radius: 6px;
}

.button-group {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin: 20px 0;
}

.button-group button {
    padding: 10px;
    border: none;
    border-radius: 6px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
}

.button-group button:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}

.button-group button:disabled {
    background: #ccc;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

.stats-container {
    background: #f8f9fa;
    border-radius: 8px;
    padding: 15px;
    margin-top: 20px;
}

.stat {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;
}

.stat-label {
    font-weight: 600;
    color: #666;
}

.stat-value {
    color: #764ba2;
    font-weight: 700;
}

.visualization-container {
    background: rgba(255, 255, 255, 0.95);
    border-radius: 12px;
    padding: 20px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

.array-container {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    height: 400px;
    padding: 20px 0;
    border-bottom: 2px solid #eee;
    margin-bottom: 20px;
}

.array-bar {
    flex: 1;
    margin: 0 2px;
    background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
    border-radius: 4px 4px 0 0;
    transition: height 0.1s ease;
    position: relative;
}

.array-bar.comparing {
    background: linear-gradient(180deg, #ff6b6b 0%, #ee5a24 100%);
}

.array-bar.swapping {
    background: linear-gradient(180deg, #f9ca24 0%, #f0932b 100%);
}

.array-bar.sorted {
    background: linear-gradient(180deg, #00b894 0%, #00cec9 100%);
}

.array-bar .value {
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 10px;
    font-weight: bold;
    color: #333;
}

.algorithm-info {
    background: #f8f9fa;
    border-radius: 8px;
    padding: 20px;
}

.algorithm-info h3 {
    color: #764ba2;
    margin-bottom: 10px;
    font-size: 1.2em;
}

.algorithm-info p {
    color: #666;
    line-height: 1.6;
    margin-bottom: 15px;
}

.complexity {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    background: white;
    padding: 10px;
    border-radius: 6px;
}

.complexity div {
    text-align: center;
    font-weight: 600;
    color: #764ba2;
}

/* 响应式设计 */
@media (max-width: 768px) {
    .app-container {
        grid-template-columns: 1fr;
    }
    
    .array-container {
        height: 300px;
    }
}

步骤3:排序算法实现

// sorters.js - 排序算法集合
const Sorters = {
    // 冒泡排序
    bubbleSort: async (array, callbacks) => {
        const arr = [...array];
        let swapped;
        
        for (let i = 0; i < arr.length; i++) {
            swapped = false;
            
            for (let j = 0; j < arr.length - i - 1; j++) {
                // 记录比较操作
                callbacks.onCompare(j, j + 1);
                await callbacks.wait();
                
                if (arr[j] > arr[j + 1]) {
                    // 记录交换操作
                    callbacks.onSwap(j, j + 1);
                    await callbacks.wait();
                    
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                    swapped = true;
                }
            }
            
            // 标记已排序
            callbacks.onSorted(arr.length - i - 1);
            
            if (!swapped) break;
        }
        
        return arr;
    },
    
    // 选择排序
    selectionSort: async (array, callbacks) => {
        const arr = [...array];
        
        for (let i = 0; i < arr.length - 1; i++) {
            let minIndex = i;
            
            for (let j = i + 1; j < arr.length; j++) {
                callbacks.onCompare(minIndex, j);
                await callbacks.wait();
                
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            
            if (minIndex !== i) {
                callbacks.onSwap(i, minIndex);
                await callbacks.wait();
                [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
            }
            
            callbacks.onSorted(i);
        }
        
        callbacks.onSorted(arr.length - 1);
        return arr;
    },
    
    // 插入排序
    insertionSort: async (array, callbacks) => {
        const arr = [...array];
        
        for (let i = 1; i < arr.length; i++) {
            let j = i;
            
            while (j > 0) {
                callbacks.onCompare(j - 1, j);
                await callbacks.wait();
                
                if (arr[j - 1] > arr[j]) {
                    callbacks.onSwap(j - 1, j);
                    await callbacks.wait();
                    [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]];
                    j--;
                } else {
                    break;
                }
            }
            
            // 标记当前位置已插入
            callbacks.onSorted(i);
        }
        
        callbacks.onSorted(0);
        return arr;
    },
    
    // 希尔排序
    shellSort: async (array, callbacks) => {
        const arr = [...array];
        let gap = Math.floor(arr.length / 2);
        
        while (gap > 0) {
            for (let i = gap; i < arr.length; i++) {
                let j = i;
                
                while (j >= gap) {
                    callbacks.onCompare(j - gap, j);
                    await callbacks.wait();
                    
                    if (arr[j - gap] > arr[j]) {
                        callbacks.onSwap(j - gap, j);
                        await callbacks.wait();
                        [arr[j - gap], arr[j]] = [arr[j], arr[j - gap]];
                        j -= gap;
                    } else {
                        break;
                    }
                }
            }
            
            gap = Math.floor(gap / 2);
        }
        
        // 标记所有元素已排序
        for (let i = 0; i < arr.length; i++) {
            callbacks.onSorted(i);
        }
        
        return arr;
    },
    
    // 归并排序
    mergeSort: async (array, callbacks, start = 0, end = array.length - 1) => {
        const arr = [...array];
        
        if (start >= end) return [arr[start]];
        
        const mid = Math.floor((start + end) / 2);
        
        // 递归排序左右两部分
        const left = await Sorters.mergeSort(arr, callbacks, start, mid);
        const right = await Sorters.mergeSort(arr, callbacks, mid + 1, end);
        
        // 合并两个有序数组
        return await Sorters.merge(left, right, callbacks, start);
    },
    
    merge: async (left, right, callbacks, startIndex) => {
        const result = [];
        let i = 0, j = 0;
        const originalArray = [...left, ...right];
        
        while (i < left.length && j < right.length) {
            // 记录比较操作
            callbacks.onCompare(startIndex + i, startIndex + left.length + j);
            await callbacks.wait();
            
            if (left[i] <= right[j]) {
                result.push(left[i++]);
            } else {
                result.push(right[j++]);
            }
        }
        
        while (i < left.length) {
            result.push(left[i++]);
        }
        
        while (j < right.length) {
            result.push(right[j++]);
        }
        
        // 更新可视化
        for (let k = 0; k < result.length; k++) {
            callbacks.onUpdate(startIndex + k, result[k]);
            await callbacks.wait();
            callbacks.onSorted(startIndex + k);
        }
        
        return result;
    },
    
    // 快速排序
    quickSort: async (array, callbacks, low = 0, high = array.length - 1) => {
        const arr = [...array];
        
        if (low < high) {
            const pivotIndex = await Sorters.partition(arr, callbacks, low, high);
            
            await Sorters.quickSort(arr, callbacks, low, pivotIndex - 1);
            await Sorters.quickSort(arr, callbacks, pivotIndex + 1, high);
        }
        
        // 标记所有元素已排序
        for (let i = low; i <= high; i++) {
            callbacks.onSorted(i);
        }
        
        return arr;
    },
    
    partition: async (array, callbacks, low, high) => {
        const pivot = array[high];
        let i = low - 1;
        
        for (let j = low; j < high; j++) {
            callbacks.onCompare(j, high);
            await callbacks.wait();
            
            if (array[j] < pivot) {
                i++;
                callbacks.onSwap(i, j);
                await callbacks.wait();
                [array[i], array[j]] = [array[j], array[i]];
            }
        }
        
        callbacks.onSwap(i + 1, high);
        await callbacks.wait();
        [array[i + 1], array[high]] = [array[high], array[i + 1]];
        
        return i + 1;
    },
    
    // 堆排序
    heapSort: async (array, callbacks) => {
        const arr = [...array];
        const n = arr.length;
        
        // 构建最大堆
        for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
            await Sorters.heapify(arr, n, i, callbacks);
        }
        
        // 逐个提取元素
        for (let i = n - 1; i > 0; i--) {
            callbacks.onSwap(0, i);
            await callbacks.wait();
            [arr[0], arr[i]] = [arr[i], arr[0]];
            
            callbacks.onSorted(i);
            await Sorters.heapify(arr, i, 0, callbacks);
        }
        
        callbacks.onSorted(0);
        return arr;
    },
    
    heapify: async (array, n, i, callbacks) => {
        let largest = i;
        const left = 2 * i + 1;
        const right = 2 * i + 2;
        
        if (left < n) {
            callbacks.onCompare(left, largest);
            await callbacks.wait();
            
            if (array[left] > array[largest]) {
                largest = left;
            }
        }
        
        if (right < n) {
            callbacks.onCompare(right, largest);
            await callbacks.wait();
            
            if (array[right] > array[largest]) {
                largest = right;
            }
        }
        
        if (largest !== i) {
            callbacks.onSwap(i, largest);
            await callbacks.wait();
            [array[i], array[largest]] = [array[largest], array[i]];
            
            await Sorters.heapify(array, n, largest, callbacks);
        }
    },
    
    // 计数排序
    countingSort: async (array, callbacks) => {
        const arr = [...array];
        const max = Math.max(...arr);
        const min = Math.min(...arr);
        const range = max - min + 1;
        
        const count = new Array(range).fill(0);
        const output = new Array(arr.length);
        
        // 统计每个元素的出现次数
        for (let i = 0; i < arr.length; i++) {
            count[arr[i] - min]++;
        }
        
        // 计算累积计数
        for (let i = 1; i < count.length; i++) {
            count[i] += count[i - 1];
        }
        
        // 从后向前遍历,构建输出数组
        for (let i = arr.length - 1; i >= 0; i--) {
            callbacks.onCompare(i, i); // 简化展示
            await callbacks.wait();
            
            output[count[arr[i] - min] - 1] = arr[i];
            count[arr[i] - min]--;
        }
        
        // 将输出数组复制回原数组
        for (let i = 0; i < arr.length; i++) {
            callbacks.onUpdate(i, output[i]);
            await callbacks.wait();
            callbacks.onSorted(i);
        }
        
        return output;
    }
};

// 导出排序器
if (typeof module !== 'undefined' && module.exports) {
    module.exports = Sorters;
}

步骤4:可视化引擎实现

// visualizer.js - 可视化引擎
class Visualizer {
    constructor(containerId) {
        this.container = document.getElementById(containerId);
        this.bars = [];
        this.array = [];
        this.isRunning = false;
        this.isPaused = false;
        this.animationSpeed = 50; // 1-100
        this.delay = this.calculateDelay(this.animationSpeed);
    }
    
    // 初始化数组可视化
    initialize(array) {
        this.array = [...array];
        this.container.innerHTML = '';
        this.bars = [];
        
        const maxValue = Math.max(...this.array);
        
        this.array.forEach((value, index) => {
            const bar = document.createElement('div');
            bar.className = 'array-bar';
            bar.style.height = `${(value / maxValue) * 100}%`;
            bar.style.transition = `height 0.1s ease`;
            
            // 添加数值标签
            const valueLabel = document.createElement('div');
            valueLabel.className = 'value';
            valueLabel.textContent = value;
            bar.appendChild(valueLabel);
            
            this.container.appendChild(bar);
            this.bars.push(bar);
        });
    }
    
    // 计算动画延迟
    calculateDelay(speed) {
        // 速度值范围1-100,映射到延迟500ms-5ms
        return Math.max(5, 500 - (speed * 5));
    }
    
    // 设置动画速度
    setAnimationSpeed(speed) {
        this.animationSpeed = speed;
        this.delay = this.calculateDelay(speed);
    }
    
    // 等待函数(支持暂停)
    async wait() {
        if (this.isPaused) {
            await new Promise(resolve => {
                this.resumeResolve = resolve;
            });
        }
        
        return new Promise(resolve => {
            setTimeout(resolve, this.delay);
        });
    }
    
    // 高亮比较的元素
    highlightCompare(index1, index2) {
        this.clearHighlights();
        
        if (this.bars[index1]) {
            this.bars[index1].classList.add('comparing');
        }
        
        if (this.bars[index2]) {
            this.bars[index2].classList.add('comparing');
        }
    }
    
    // 标记交换
    highlightSwap(index1, index2) {
        this.clearHighlights();
        
        if (this.bars[index1]) {
            this.bars[index1].classList.add('swapping');
        }
        
        if (this.bars[index2]) {
            this.bars[index2].classList.add('swapping');
        }
        
        // 交换高度
        const height1 = this.bars[index1].style.height;
        const height2 = this.bars[index2].style.height;
        
        this.bars[index1].style.height = height2;
        this.bars[index2].style.height = height1;
        
        // 交换数值标签
        const value1 = this.bars[index1].querySelector('.value');
        const value2 = this.bars[index2].querySelector('.value');
        
        if (value1 && value2) {
            const temp = value1.textContent;
            value1.textContent = value2.textContent;
            value2.textContent = temp;
        }
    }
    
    // 更新元素值
    updateValue(index, value) {
        if (this.bars[index]) {
            const maxValue = Math.max(...this.array);
            this.bars[index].style.height = `${(value / maxValue) * 100}%`;
            
            const valueLabel = this.bars[index].querySelector('.value');
            if (valueLabel) {
                valueLabel.textContent = value;
            }
            
            this.array[index] = value;
        }
    }
    
    // 标记已排序
    markSorted(index) {
        if (this.bars[index]) {
            this.bars[index].classList.remove('comparing', 'swapping');
            this.bars[index].classList.add('sorted');
        }
    }
    
    // 清除所有高亮
    clearHighlights() {
        this.bars.forEach(bar => {
            bar.classList.remove('comparing', 'swapping');
        });
    }
    
    // 重置所有状态
    reset() {
        this.isRunning = false;
        this.isPaused = false;
        this.clearHighlights();
        
        this.bars.forEach(bar => {
            bar.classList.remove('sorted', 'comparing', 'swapping');
        });
    }
    
    // 暂停动画
    pause() {
        this.isPaused = true;
    }
    
    // 恢复动画
    resume() {
        this.isPaused = false;
        if (this.resumeResolve) {
            this.resumeResolve();
            this.resumeResolve = null;
        }
    }
    
    // 获取回调函数
    getCallbacks() {
        return {
            onCompare: (i, j) => {
                this.highlightCompare(i, j);
                this.comparisonCount++;
            },
            onSwap: (i, j) => {
                this.highlightSwap(i, j);
                this.swapCount++;
            },
            onUpdate: (i, value) => {
                this.updateValue(i, value);
            },
            onSorted: (i) => {
                this.markSorted(i);
            },
            wait: () => this.wait()
        };
    }
    
    // 重置统计计数
    resetCounts() {
        this.comparisonCount = 0;
        this.swapCount = 0;
    }
}

// 导出可视化器
if (typeof module !== 'undefined' && module.exports) {
    module.exports = Visualizer;
}

步骤5:主应用逻辑

// app.js - 主应用逻辑
document.addEventListener('DOMContentLoaded', () => {
    // DOM元素引用
    const algorithmSelect = document.getElementById('algorithm');
    const speedSlider = document.getElementById('speed');
    const speedValue = document.getElementById('speed-value');
    const arraySizeSlider = document.getElementById('array-size');
    const arraySizeValue = document.getElementById('array-size-value');
    const generateBtn = document.getElementById('generate');
    const startBtn = document.getElementById('start');
    const pauseBtn = document.getElementById('pause');
    const resumeBtn = document.getElementById('resume');
    const resetBtn = document.getElementById('reset');
    const comparisonsDisplay = document.getElementById('comparisons');
    const swapsDisplay = document.getElementById('swaps');
    const statusDisplay = document.getElementById('status');
    const algorithmNameDisplay = document.getElementById('algorithm-name');
    const algorithmDescriptionDisplay = document.getElementById('algorithm-description');
    
    // 初始化可视化器
    const visualizer = new Visualizer('array-container');
    
    // 算法信息
    const algorithmInfo = {
        bubble: {
            name: '冒泡排序',
            description: '通过重复遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。',
            timeComplexity: 'O(n²)',
            spaceComplexity: 'O(1)'
        },
        selection: {
            name: '选择排序',
            description: '首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。',
            timeComplexity: 'O(n²)',
            spaceComplexity: 'O(1)'
        },
        insertion: {
            name: '插入排序',
            description: '通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序)。',
            timeComplexity: 'O(n²)',
            spaceComplexity: 'O(1)'
        },
        shell: {
            name: '希尔排序',
            description: '也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;2. 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。',
            timeComplexity: 'O(n log n)',
            spaceComplexity: 'O(1)'
        },
        merge: {
            name: '归并排序',
            description: '采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。',
            timeComplexity: 'O(n log n)',
            spaceComplexity: 'O(n)'
        },
        quick: {
            name: '快速排序',
            description: '通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。',
            timeComplexity: 'O(n log n)',
            spaceComplexity: 'O(log n)'
        },
        heap: {
            name: '堆排序',
            description: '利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。',
            timeComplexity: 'O(n log n)',
            spaceComplexity: 'O(1)'
        },
        counting: {
            name: '计数排序',
            description: '一个非基于比较的排序算法,该算法于1954年由Harold H. Seward提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序。',
            timeComplexity: 'O(n + k)',
            spaceComplexity: 'O(n + k)'
        }
    };
    
    // 当前状态
    let state = {
        array: [],
        isSorting: false,
        currentAlgorithm: null,
        arraySize: 20
    };
    
    // 生成随机数组
    function generateRandomArray(size) {
        const array = [];
        for (let i = 0; i < size; i++) {
            array.push(Math.floor(Math.random() * 100) + 1);
        }
        return array;
    }
    
    // 更新显示信息
    function updateDisplay() {
        comparisonsDisplay.textContent = visualizer.comparisonCount || 0;
        swapsDisplay.textContent = visualizer.swapCount || 0;
    }
    
    // 更新算法信息
    function updateAlgorithmInfo(algorithm) {
        const info = algorithmInfo[algorithm];
        algorithmNameDisplay.textContent = info.name;
        algorithmDescriptionDisplay.textContent = info.description;
        
        const complexityDiv = document.querySelector('.complexity');
        complexityDiv.innerHTML = `
            <div>时间复杂度: ${info.timeComplexity}</div>
            <div>空间复杂度: ${info.spaceComplexity}</div>
        `;
    }
    
    // 重置应用状态
    function resetState() {
        state.isSorting = false;
        visualizer.reset();
        visualizer.resetCounts();
        updateDisplay();
        
        startBtn.disabled = false;
        pauseBtn.disabled = true;
        resumeBtn.disabled = true;
        statusDisplay.textContent = '就绪';
    }
    
    // 开始排序
    async function startSorting() {
        if (state.isSorting) return;
        
        state.isSorting = true;
        state.currentAlgorithm = algorithmSelect.value;
        
        // 更新UI状态
        startBtn.disabled = true;
        pauseBtn.disabled = false;
        resumeBtn.disabled = true;
        statusDisplay.textContent = '排序中...';
        
        // 更新算法信息
        updateAlgorithmInfo(state