告别抽象!可视化动画带你学习算法——选择排序

选择排序是一种简单直观的排序算法,非常适合编程入门者学习。它的基本思想是:每次找出数组中最小(或最大)的元素,然后将其放到数组的起始位置,接着再从剩余未排序的元素中寻找最小(或最大)的元素,放到已排序序列的末尾,依此类推,直到所有元素排序完毕。

一、核心思想:"逐个挑选,放到对应位置"

选择排序的逻辑就像我们日常生活中整理书架 的过程 ------ 假设要把一摞杂乱的书按厚度从薄到厚排列,我们会先从所有书中找出最薄的那本,放到书架的第一个位置;接着从剩下的书中再找出最薄的,放到第二个位置;以此类推,直到所有书都排列整齐。

用算法的语言来描述,选择排序的核心思想可以概括为两点:

划分区间:将待排序数组分为 "已排序区间" 和 "未排序区间"。初始时,已排序区间为空,未排序区间为整个数组。

选择与交换:在每一轮排序中,从 "未排序区间" 里找到最小(或最大,取决于排序方向)的元素,将其与 "未排序区间" 的第一个元素交换位置,此时该元素就进入了 "已排序区间"。重复这一步骤,直到未排序区间为空。

举个具体的例子:

对数组 [64, 25, 12, 22, 11] 进行升序排序,选择排序的过程如下:

初始状态:已排序区间 [],未排序区间 [64, 25, 12, 22, 11]

第 1 轮:找到未排序区间最小元素 11,与第一个元素 64 交换 → 数组变为 [11, 25, 12, 22, 64],已排序区间 [11]

第 2 轮:从剩余未排序区间 [25, 12, 22, 64] 中找到最小元素 12,与未排序区间第一个元素 25 交换 → 数组变为 [11, 12, 25, 22, 64],已排序区间 [11, 12]

第 3 轮:从剩余未排序区间 [25, 22, 64] 中找到最小元素 22,与未排序区间第一个元素 25 交换 → 数组变为 [11, 12, 22, 25, 64],已排序区间 [11, 12, 22]

第 4 轮:从剩余未排序区间 [25, 64] 中找到最小元素 25,无需交换(本身就在未排序区间第一个位置)→ 数组不变,已排序区间 [11, 12, 22, 25, 64],排序完成

从过程中能看出,选择排序的 选择 体现在找最小元素排序 体现在交换到对应位置

二、代码实现

js 复制代码
// 选择排序函数
// 参数:需要排序的数组
function selectionSort(arr) {
  // 获取数组长度
  const length = arr.length;

  // 外层循环:控制需要排序的轮数
  // 每一轮会确定一个位置的正确元素
  for (let i = 0; i < length - 1; i++) {
    // 假设当前轮次的第一个元素是最小值
    let minIndex = i;

    // 内层循环:寻找当前未排序部分的最小值
    // 从i+1开始,因为i之前的元素已经排好序了
    for (let j = i + 1; j < length; j++) {
      // 如果找到更小的元素,更新最小值的索引
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }

    // 如果最小值不是当前位置的元素,则交换它们
    if (minIndex !== i) {
       let temp = arr[i];
       arr[i] = arr[minIndex];
       arr[minIndex] = temp;
    }

    // 打印每一轮排序后的结果,方便观察排序过程
    console.log(`第 ${i+1}轮排序后:`, [...arr]);
  }

  // 返回排序后的数组
  return arr;
}

// 测试案例
const numbers = [64, 25, 12, 22, 11];
console.log("原始数组:", numbers);

// 调用选择排序函数
const sortedNumbers = selectionSort(numbers);

// 输出排序结果
console.log("排序后数组:", sortedNumbers);

//原始数组: [64, 25, 12, 22, 11]
//第1轮排序后: [11, 25, 12, 22, 64]
//第2轮排序后: [11, 12, 25, 22, 64]
//第3轮排序后: [11, 12, 22, 25, 64]
//第4轮排序后: [11, 12, 22, 25, 64]
//排序后数组: [11, 12, 22, 25, 64]

算法复杂度分析 ​

选择排序的时间复杂度和空间复杂度都比较固定,具体分析如下:

时间复杂度:无论数组是否有序,都需要遍历 "未排序区间" 找最小值,因此最好、最坏、平均时间复杂度均为 O(n²)(n 为数组长度)。这是选择排序的最大缺点 ------ 即使面对已排序数组,也无法减少遍历次数。

空间复杂度:排序过程中仅使用了 min_index、temp 等少量临时变量,未使用额外的数组或数据结构,因此空间复杂度为 O(1),属于 "原地排序" 算法。

选择排序的应用场景

  1. 小规模数据排序

  2. 内存资源有限的场景

选择排序可视化

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>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            line-height: 1.6;
        }

        .array-container {
            display: flex;
            margin: 20px 0;
            justify-content: center;
        }

        .array-element {
            width: 50px;
            height: 50px;
            background-color: #4CAF50;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0 5px;
            border-radius: 5px;
            font-weight: bold;
            transition: all 0.3s ease;
        }

        .current-min {
            background-color: #FF9800;
        }

        .sorted {
            background-color: #2196F3;
        }

        .comparing {
            background-color: #f44336;
        }

        .code-block {
            background-color: #f5f5f5;
            padding: 15px;
            border-radius: 5px;
            margin: 15px 0;
            overflow-x: auto;
        }

        button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 15px;
            margin: 5px;
            border-radius: 4px;
            cursor: pointer;
        }

        button:hover {
            background-color: #45a049;
        }

        .explanation {
            background-color: #e7f3ff;
            padding: 15px;
            border-left: 4px solid #2196F3;
            margin: 15px 0;
        }
    </style>
</head>

<body>
    <h1>选择排序可视化教程</h1>

    <div class="explanation">
        <h3>选择排序的核心思想:</h3>
        <p>每次循环找到最小的元素,然后把它放到正确的位置上。</p>
        <p>就像整理扑克牌:每次从手中找到最小的牌,然后按顺序放好。</p>
    </div>

    <div class="code-block">
        <h3>选择排序JavaScript代码:</h3>
        <pre><code>
    function selectionSort(arr) {
        let n = arr.length;

        // 外层循环控制排序的轮数
        for (let i = 0; i < n - 1; i++) {
            // 假设当前轮次的第一个元素是最小的
            let minIndex = i;

            // 内层循环在未排序部分中寻找最小的元素
            for (let j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j; // 更新最小元素的索引
                }
            }

            // 将找到的最小元素与当前轮次的第一个元素交换
            if (minIndex !== i) {
                [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
            }
        }

        return arr;
    }
        </code></pre>
    </div>

    <h3>可视化演示:</h3>
    <div id="array-container" class="array-container"></div>

    <div>
        <button onclick="startVisualization()">开始演示</button>
        <button onclick="resetArray()">重置数组</button>
        <button onclick="stepByStep()">单步执行</button>
    </div>

    <div id="status" style="margin-top: 15px; font-weight: bold;"></div>

    <div class="explanation">
        <h3>算法步骤详解:</h3>
        <ol>
            <li>从第一个元素开始,假设它是最小的</li>
            <li>与后面的所有元素比较,找到真正的最小元素</li>
            <li>将最小元素与第一个元素交换位置</li>
            <li>对剩下的未排序元素重复上述过程</li>
        </ol>
    </div>

    <script>
        // 初始数组
        let array = [64, 25, 12, 22, 11];
        let originalArray = [...array];

        // 算法状态
        let currentStep = 0;
        let i = 0;
        let j = 0;
        let minIndex = 0;
        let isSorting = false;
        let isCompleted = false;

        // 渲染数组到页面
        function renderArray(highlightIndexes = {}, status = "") {
            const container = document.getElementById('array-container');
            container.innerHTML = '';

            array.forEach((value, index) => {
                const element = document.createElement('div');
                element.className = 'array-element';
                element.textContent = value;

                if (index < i) {
                    element.classList.add('sorted');
                } else if (index === highlightIndexes.currentMin) {
                    element.classList.add('current-min');
                } else if (index === highlightIndexes.comparing) {
                    element.classList.add('comparing');
                }

                container.appendChild(element);
            });

            document.getElementById('status').textContent = status;
        }

        // 初始化渲染
        renderArray({}, "点击'开始演示'按钮观看选择排序过程");

        // 开始可视化演示
        function startVisualization() {
            if (isCompleted) {
                resetArray();
            }

            isSorting = true;
            currentStep = 0;
            i = 0;
            j = 0;
            minIndex = 0;

            const sortInterval = setInterval(() => {
                if (!performStep()) {
                    clearInterval(sortInterval);
                    isSorting = false;
                    isCompleted = true;
                }
            }, 1000);
        }

        // 单步执行
        function stepByStep() {
            if (isCompleted) {
                resetArray();
            }

            performStep();
        }

        // 执行一步排序
        function performStep() {
            const n = array.length;

            if (i < n - 1) {
                if (currentStep === 0) {
                    // 步骤1: 初始化minIndex为当前i
                    minIndex = i;
                    j = i + 1;
                    renderArray({ currentMin: minIndex }, `第 ${i + 1}轮: 设置最小值为位置 ${i}的元素(${array[i]})`);
                    currentStep = 1;
                    return true;
                } else if (currentStep === 1 && j < n) {
                    // 步骤2: 比较当前元素与最小值
                    if (array[j] < array[minIndex]) {
                        minIndex = j;
                        renderArray({ currentMin: minIndex, comparing: j }, `第 ${i + 1}轮: 发现更小的值 ${array[j]},更新最小值为位置 ${j}`);
                    } else {
                        renderArray({ currentMin: minIndex, comparing: j }, `第 ${i + 1}轮: 比较位置 ${j}的值 ${array[j]},当前最小值仍是 ${array[minIndex]}`);
                    }
                    j++;

                    if (j >= n) {
                        currentStep = 2;
                    }
                    return true;
                } else if (currentStep === 2) {
                    // 步骤3: 交换元素
                    if (minIndex !== i) {
                        [array[i], array[minIndex]] = [array[minIndex], array[i]];
                        renderArray({}, `第 ${i + 1}轮: 交换位置 ${i}和位置 ${minIndex}的元素`);
                    } else {
                        renderArray({}, `第 ${i + 1}轮: 最小值已在正确位置,无需交换`);
                    }
                    i++;
                    currentStep = 0;
                    return true;
                }
            } else {
                renderArray({}, "排序完成!");
                return false;
            }
        }

        // 重置数组
        function resetArray() {
            array = [...originalArray];
            currentStep = 0;
            i = 0;
            j = 0;
            minIndex = 0;
            isSorting = false;
            isCompleted = false;
            renderArray({}, "数组已重置,点击'开始演示'按钮观看选择排序过程");
        }
    </script>
</body>

</html>

各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!

相关推荐
毕设十刻2 小时前
基于vue的考研信息系统6kv17(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
望获linux3 小时前
论文解读:利用中断隔离技术的 Linux 亚微秒响应性能优化
java·linux·运维·前端·arm开发·数据库·性能优化
brzhang3 小时前
ChatGPT Pulse来了:AI 每天替你做研究,这事儿你该高兴还是该小心?
前端·后端·架构
正义的大古3 小时前
OpenLayers地图交互 -- 章节八:平移交互详解
javascript·vue.js·交互·openlayers
泉城老铁3 小时前
springboot+vue 文件下载,实现大文件的分片压缩和下载,避免内存溢出
前端·spring boot·后端
用户203735549813 小时前
Vue+Node+MongoDB高级全栈开发视频教程 完整版
前端
我是天龙_绍3 小时前
setup 函数 和 setup 语法糖
前端
泉城老铁3 小时前
Spring Boot和Vue.js项目中实现文件压缩下载功能
前端·spring boot·后端
我是天龙_绍3 小时前
vue3 中,setup 函数 和 <script setup> 的区别
前端