Javascript排序算法(冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序)详解

news/2025/2/23 6:25:44

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

JS 算法>排序算法详解

算法>排序算法是计算机科学中的基础,用于将一组数据按照某种顺序重新排列。JavaScript中常见的算法>排序算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序等。以下是这些算法的详细介绍和代码示例。

冒泡排序(Bubble Sort)

冒泡排序是一种简单的算法>排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素为止,这时数列就完全排序好了。

代码示例

javascript">function bubbleSort(arr) {
    let len = arr.length;
    for (let i = 0; i < len; i++) {
        for (let j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换元素
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;
}
快速排序(Quick Sort Pivot)

快速排序的基本思想是通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
在这里插入图片描述

代码示例

javascript">function quickSort(arr) {
    if (arr.length <= 1) return arr;
    let pivot = arr[Math.floor(arr.length / 2)];
    let left = [];
    let right = [];
    for (let i = 0; i < arr.length; i++) {
        if (i === Math.floor(arr.length / 2)) continue;
        if (arr[i] < pivot) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return [...quickSort(left), pivot, ...quickSort(right)];
}
选择排序(Selection Sort)

选择排序的工作原理是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
在这里插入图片描述

代码示例

javascript">function selectionSort(arr) {
    let len = arr.length;
    for (let i = 0; i < len - 1; i++) {
        let minIndex = i;
        for (let j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        // 交换元素
        [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
    }
    return arr;
}
插入排序(Insertion Sort)

插入排序的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
在这里插入图片描述

代码示例

javascript">function insertionSort(arr) {
    let len = arr.length;
    for (let i = 1; i < len; i++) {
        let key = arr[i];
        let j = i - 1;
        // 将大于key的元素向后移动一位
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
    return arr;
}

希尔排序(Shell Sort)详解

希尔排序是插入排序的高效改进版,通过分组插入逐步缩小增量的策略减少元素移动次数,提升排序效率。

在这里插入图片描述


核心思想
  1. 增量分组:将数组按一定间隔(增量)划分为多个子序列,对每个子序列进行插入排序。
  2. 逐步缩小增量:随着增量逐渐减小,子序列逐渐变长,最终增量为1时,整个数组变为一个序列,完成排序。

实现步骤
  1. 选择增量序列:常见方法为初始增量取数组长度的一半,之后每次减半,直到增量为1。
  2. 分组插入排序:对每个增量下的子序列进行插入排序。
  3. 重复缩小增量:直到增量为1时,最后一次全数组插入排序。

JavaScript代码实现
javascript">function shellSort(arr) {
    const length = arr.length;
    let gap = Math.floor(length / 2); // 初始增量

    // 逐步缩小增量
    while (gap > 0) {
        // 对每个子序列进行插入排序
        for (let i = gap; i < length; i++) {
            const temp = arr[i];
            let j = i;
            // 插入排序逻辑
            while (j >= gap && arr[j - gap] > temp) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = temp;
        }
        gap = Math.floor(gap / 2); // 增量减半
    }
    return arr;
}

// 示例
const array = [9, 7, 6, 15, 3, 2, 8, 5];
console.log(shellSort(array)); // 输出:[2, 3, 5, 6, 7, 8, 9, 15]

关键点解析
  1. 时间复杂度

    • 平均:O(n log n) ~ O(n²),取决于增量序列的选择。
    • 最优:O(n log n)(如使用Hibbard增量序列)。
    • 最差:O(n²)(当增量序列不合理时)。
  2. 空间复杂度:O(1),原地排序无需额外空间。

  3. 稳定性:不稳定排序,相同元素可能在分组插入时改变顺序。


增量序列的影响
  • 常见增量序列
    • Shell增量:gap = n/2, gap /= 2(简单但效率一般)。
    • Hibbard增量:gap = 2^k -1(时间复杂度优化至O(n^{3/2}))。
    • Sedgewick增量:结合数学优化,性能更优(时间复杂度O(n^{4/3}))。

应用场景
  • 中小规模数据:在数据量不大时,希尔排序比传统O(n log n)算法(如快速排序)更快。
  • 内存敏感场景:原地排序特性适合内存受限环境。

与插入排序对比
特性插入排序希尔排序
时间复杂度O(n²)O(n log n) ~ O(n²)
数据移动次数高(逐元素移动)低(分组跳跃式移动)
适用场景小规模或基本有序数据中等规模无序数据

通过分组插入优化,希尔排序在减少元素移动次数和提升效率之间找到了平衡,是经典算法>排序算法中的实用选择。

归并排序(Merge Sort)

在这里插入图片描述

归并排序是建立在归并操作上的一种有效的算法>排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

代码示例

javascript">function mergeSort(arr) {
    if (arr.length <= 1) return arr;
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    return merge(left, right);
}

function merge(left, right) {
    let result = [];
    while (left.length && right.length) {
        if (left[0] < right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    return result.concat(left, right);
}
堆排序

在这里插入图片描述
在这里插入图片描述

JavaScript中的堆排序是一种基于堆数据结构的算法>排序算法,主要利用最大堆(升序)或最小堆(降序)的性质进行高效排序。以下是详细解析:


堆排序原理
  1. 堆的定义
    堆是一种完全二叉树,分为最大堆和最小堆:

    • 最大堆:每个父节点的值都大于或等于其子节点的值,根节点为最大值。
    • 最小堆:每个父节点的值都小于或等于其子节点的值,根节点为最小值。
  2. 核心步骤

    • 构建初始堆:将无序数组调整为一个最大堆(升序)或最小堆(降序)。
    • 交换与调整:将堆顶元素(最大值或最小值)与末尾元素交换,缩小堆的范围,重新调整剩余元素为堆。重复此过程直到整个数组有序。

具体实现步骤
  1. 构建最大堆(以升序为例)

    • 从最后一个非叶子节点开始:最后一个非叶子节点的索引为 Math.floor(array.length / 2 - 1)。从该节点向上遍历,调整每个子树使其满足最大堆性质。
    • 调整方法(Heapify) :比较父节点与左右子节点的值,若子节点更大则交换,并递归调整受影响的子树。
  2. 交换堆顶与末尾元素

    • 将堆顶元素(当前最大值)与堆末尾元素交换,此时末尾元素已排序。
    • 缩小堆范围:排除已排序的末尾元素,对剩余元素重新调整堆。
  3. 重复调整与交换
    直到堆中仅剩一个元素,此时数组完全有序。
    在这里插入图片描述


JavaScript代码实现
javascript">// 调整最大堆
function heapify(arr, length, i) {
    let largest = i;         // 当前根节点
    let left = 2 * i + 1;    // 左子节点索引
    let right = 2 * i + 2;   // 右子节点索引

    // 比较左子节点与根节点
    if (left < length && arr[left] > arr[largest]) {
        largest = left;
    }
    // 比较右子节点与当前最大值
    if (right < length && arr[right] > arr[largest]) {
        largest = right;
    }
    // 若最大值不是根节点,交换并递归调整
    if (largest !== i) {
        [arr[i], arr[largest]] = [arr[largest], arr[i]];
        heapify(arr, length, largest); // 调整受影响的子树[[5, 12]]
    }
}

// 堆排序主函数
function heapSort(arr) {
    const length = arr.length;
    // 构建初始最大堆(从最后一个非叶子节点开始)
    for (let i = Math.floor(length / 2 - 1); i >= 0; i--) {
        heapify(arr, length, i); // [[9, 12]]
    }
    // 逐个交换堆顶与末尾元素,并调整堆
    for (let i = length - 1; i > 0; i--) {
        [arr[0], arr[i]] = [arr[i], arr[0]]; // 交换堆顶和末尾元素[[4, 7]]
        heapify(arr, i, 0); // 调整剩余元素为堆[[5, 13]]
    }
    return arr;
}

// 示例
const array = [3, 1, 5, 7, 2, 4, 9, 6];
console.log(heapSort(array)); // 输出:[1, 2, 3, 4, 5, 6, 7, 9]

关键点解析
  • 时间复杂度:构建堆的时间为O(n),每次调整堆为O(log n),总时间复杂度为O(n log n)。
  • 空间复杂度:原地排序,仅需常数级额外空间,空间复杂度O(1)。
  • 稳定性:堆排序是不稳定的算法,相同值的元素在排序后可能改变相对位置。

应用场景
  • 大规模数据排序:时间复杂度为O(n log n),适合处理大数据集。
  • 优先队列:堆结构常用于实现优先队列快速获取最大/最小值

通过上述步骤和代码,可以清晰地理解堆排序在JavaScript中的实现逻辑及其高效性。

常见算法>排序算法题目和代码

  1. 题目:对一个数组进行排序,要求时间复杂度尽可能低。

解答:可以使用快速排序或归并排序,因为它们的平均时间复杂度都是O(n log n)。

javascript">// 使用快速排序
let arr = [3, 6, 8, 10, 1, 2, 1];
console.log(quickSort(arr));

// 或者使用归并排序
let arr2 = [3, 6, 8, 10, 1, 2, 1];
console.log(mergeSort(arr2));
  1. 题目:实现一个稳定的算法>排序算法

解答:归并排序是稳定的算法>排序算法,因为它在合并两个有序子序列时,如果两个元素相等,它会保持它们在原数组中的相对顺序。

javascript">let arr = [3, 3, 2, 1];
console.log(mergeSort(arr)); // 输出 [1, 2, 3, 3]
  1. 题目:对一个几乎已经有序的数组进行排序,要求时间复杂度尽可能低。

解答:对于几乎已经有序的数组,插入排序的时间复杂度可以接近O(n),因为它在每次插入时只需要移动少量的元素。

javascript">let arr = [1, 2, 3, 4, 5, 6, 8, 7];
console.log(insertionSort(arr)); // 输出 [1, 2, 3, 4, 5, 6, 7, 8]
  1. 题目:找出数组中的第k小的元素。

解答:可以使用快速选择算法(Quickselect),它是快速排序的一种变体,用于在未排序的数组中找到第k小的元素。

javascript">function quickSelect(arr, k) {
    if (arr.length === 1) return arr[0];
    let pivot = arr[Math.floor(Math.random() * arr.length)];
    let lows = [];
    let highs = [];
    let pivots = [];
    for (let num of arr) {
        if (num < pivot) lows.push(num);
        else if (num > pivot) highs.push(num);
        else pivots.push(num);
    }
    if (k < lows.length) return quickSelect(lows, k);
    else if (k < lows.length + pivots.length) return pivots[0];
    else return quickSelect(highs, k - lows.length - pivots.length);
}

let arr = [3, 2, 1, 5, 6, 4];
console.log(quickSelect(arr, 2)); // 输出 2

以上就是对JavaScript中算法>排序算法的详细介绍和常见题目的解答。


http://www.niftyadmin.cn/n/5863110.html

相关文章

GPIO外设

一、GPIO简介 GPIO&#xff0c;general-purpos IO port,通用输入输出引脚&#xff0c;所有的GPIO引脚都有基本的输入输出功能。 最基本的输出功能&#xff1a;STM32控制引脚输出高、低电平&#xff0c;实现开关控制&#xff1b;最基本的输入功能&#xff1a;检测外部输入电平&…

CDGA|企业数据治理实战:从疏通“信息河”到打造优质“数据湖”

在当今的数字化时代&#xff0c;数据已成为企业的重要资产&#xff0c;其价值不言而喻。然而&#xff0c;面对海量的数据&#xff0c;如何进行有效的治理&#xff0c;将其转化为企业的竞争优势&#xff0c;成为了众多企业面临的难题。本文将深入探讨企业数据治理的实战策略&…

[实现Rpc] 服务端 | RpcRouter实现 | Builder模式

目录 项目服务端独用类的实现 1. RpcRouter类的实现 ServiceDescribe SDescribeFactory ⭕ Builder模式 1. 动机 2. 模式定义 3. 要点总结 4. 代码感受 ServiceManager RpcRouter 4. 代码感受 ServiceManager RpcRouter 前文我们就将 Rpc 通用类都实现完啦&#…

MySQL 架构

目录 1. MySQL 架构概览 (1) 客户端/服务器架构 (2) 存储引擎架构 2. 主要组件 (1) 客户端工具 (2) MySQL 服务器 (3) 存储引擎 3. MySQL 架构图 4. MySQL 架构的特点 5. MySQL 的高级架构 (1) 主从复制&#xff08;Master-Slave Replication&#xff09; (2) 主主…

透彻理解:方差、协方差、相关系数、协方差矩阵及其应用

最近看了几篇跨领域特征对齐方面的经典文献&#xff0c;学者们搞了很多花样&#xff0c;如有的提出一阶统计特征对齐&#xff0c;有的提出二阶统计特征对齐&#xff0c;有的学者提出高阶统计特征对齐。 通俗而言&#xff0c;就是在统计特征层面对跨域特征进行对齐&#xff0c;…

.NET MVC实现电影票管理

.NET MVC&#xff08;Model-View-Controller&#xff09;是微软推出的基于 Model-View-Controller 设计模式的 Web 应用框架&#xff0c;属于 ASP.NET Core 的重要组成部分。其核心目标是通过清晰的分层架构实现 高内聚、低耦合 的开发模式&#xff0c;适用于构建可扩展的企业级…

【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)

必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。云边有个稻草人-CSDN博客 对于本节我们要提前掌握前一节课堆的相关实现才能学好本次的知识&#xff0c;一定要多画图多敲代码看看实现的效果是啥&#xff08;Crazy&#xff01;&#xff09;开始吧&#xff01; …

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过…