JavaScript中的10種排序算法:從入門到精通

作為前端開發者,排序算法是我們必須掌握的基礎知識。無論是在面試中,還是在實際開發中處理數據展示時,排序都是一個常見需求。今天,我將用通俗易懂的方式,帶你了解JavaScript中最常見的10種排序算法。

1. 冒泡排序 - 最直觀的排序方式

冒泡排序可能是最容易理解的排序算法了。它的基本思想是:重復地遍歷要排序的數組,一次比較兩個元素,如果它們的順序錯誤就交換它們

想象一下水中的氣泡,較大的氣泡會慢慢浮到水面。在冒泡排序中,較大的元素會"冒泡"到數組的末端。

function bubbleSort(arr) {const n = arr.length;for (let i = 0; i < n - 1; i++) {let swapped = false; // 優化:如果一輪沒有交換,說明已經有序for (let j = 0; j < n - 1 - i; j++) {if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // ES6解構賦值交換元素swapped = true;}}if (!swapped) break; // 如果沒有發生交換,提前結束}return arr;
}

特點

  • 時間復雜度:最好情況O(n)(已經有序),最壞O(n2)
  • 空間復雜度:O(1)(原地排序)
  • 穩定排序(相等元素不會改變相對位置)

適用場景:數據量小,或者作為學習排序的入門算法。

2. 選擇排序 - 每次選擇最小的元素

選擇排序的思想是:每次從未排序部分選擇最小的元素,放到已排序部分的末尾

function selectionSort(arr) {const n = arr.length;for (let i = 0; i < n; i++) {let minIndex = i; // 假設當前索引是最小值的索引for (let j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j; // 找到更小的值,更新索引}}[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; // 交換}return arr;
}

特點

  • 時間復雜度:始終O(n2)
  • 空間復雜度:O(1)
  • 不穩定排序(可能改變相等元素的相對位置)

適用場景:數據量小,且不要求穩定排序的情況。

3. 插入排序 - 撲克牌排序方式

插入排序類似于我們整理撲克牌的方式:將數組分為已排序和未排序兩部分,每次從未排序部分取出一個元素,插入到已排序部分的正確位置

function insertionSort(arr) {for (let i = 1; i < arr.length; i++) {let current = arr[i]; // 當前要插入的元素let j = i - 1;// 將比current大的元素向后移動while (j >= 0 && arr[j] > current) {arr[j + 1] = arr[j];j--;}arr[j + 1] = current; // 插入到正確位置}return arr;
}

特點

  • 時間復雜度:最好O(n)(已經有序),最壞O(n2)
  • 空間復雜度:O(1)
  • 穩定排序

適用場景:小規模數據,或者基本有序的數據。

4. 希爾排序 - 插入排序的改進版

希爾排序是插入排序的改進版本,也稱為"縮小增量排序"。它通過將原始數組分成若干子序列進行插入排序,然后逐步縮小增量直至整個數組有序

function shellSort(arr) {let gap = Math.floor(arr.length / 2); // 初始步長while (gap > 0) {for (let i = gap; i < arr.length; i++) {let 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;
}

特點

  • 時間復雜度:平均O(n^1.3),比O(n2)好很多
  • 空間復雜度:O(1)
  • 不穩定排序

適用場景:中等規模的數據排序。

5. 歸并排序 - 分而治之的經典算法

歸并排序采用分治法的思想:將數組分成兩半,分別排序,然后將兩個有序數組合并成一個有序數組

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) {const result = [];while (left.length && right.length) {// 比較兩個數組的第一個元素,取較小的放入結果result.push(left[0] < right[0] ? left.shift() : right.shift());}return result.concat(left, right); // 合并剩余元素
}

特點

  • 時間復雜度:始終O(n log n)
  • 空間復雜度:O(n)(需要額外的存儲空間)
  • 穩定排序

適用場景:大規模數據排序,特別是需要穩定排序的情況。

6. 堆排序 - 基于二叉堆的排序

堆排序是一種基于**二叉堆(優先隊列)**的排序算法。它首先構建一個最大堆,然后不斷取出堆頂元素(最大值)放到數組末尾。

function heapSort(arr) {const n = arr.length;// 構建最大堆function heapify(i, heapSize) {let largest = i; // 初始化最大值為當前節點const left = 2 * i + 1; // 左子節點const right = 2 * i + 2; // 右子節點// 如果左子節點存在且大于當前最大值if (left < heapSize && arr[left] > arr[largest]) {largest = left;}// 如果右子節點存在且大于當前最大值if (right < heapSize && arr[right] > arr[largest]) {largest = right;}// 如果最大值不是當前節點,交換并繼續堆化if (largest !== i) {[arr[i], arr[largest]] = [arr[largest], arr[i]];heapify(largest, heapSize);}}// 構建最大堆(從最后一個非葉子節點開始)for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {heapify(i, n);}// 逐個取出堆頂元素for (let i = n - 1; i > 0; i--) {[arr[0], arr[i]] = [arr[i], arr[0]]; // 將堆頂元素(最大值)與當前末尾交換heapify(0, i); // 對剩余元素重新堆化}return arr;
}

特點

  • 時間復雜度:始終O(n log n)
  • 空間復雜度:O(1)
  • 不穩定排序

適用場景:需要高效排序且內存有限的情況。

7. 快速排序 - 最常用的排序算法

快速排序也是一種分治法的排序算法。它選擇一個"基準"元素,將數組分為兩部分:小于基準的和大于基準的,然后遞歸地對這兩部分進行排序。

function quickSort(arr) {if (arr.length <= 1) return arr; // 基線條件const pivot = arr[0]; // 選擇第一個元素作為基準(簡單實現)const left = []; // 小于基準的元素const right = []; // 大于基準的元素for (let i = 1; i < arr.length; i++) {if (arr[i] < pivot) {left.push(arr[i]);} else {right.push(arr[i]);}}return [...quickSort(left), pivot, ...quickSort(right)]; // 遞歸排序并合并
}

更高效的實現(原地分區):

function quickSort(arr, left = 0, right = arr.length - 1) {if (left < right) {const pivotIndex = partition(arr, left, right); // 獲取分區點quickSort(arr, left, pivotIndex - 1); // 遞歸排序左半部分quickSort(arr, pivotIndex + 1, right); // 遞歸排序右半部分}return arr;
}function partition(arr, left, right) {const pivot = arr[right]; // 選擇最右邊的元素作為基準let i = left; // i是小于基準的元素的邊界for (let j = left; j < right; j++) {if (arr[j] < pivot) {[arr[i], arr[j]] = [arr[j], arr[i]]; // 交換元素i++; // 移動邊界}}[arr[i], arr[right]] = [arr[right], arr[i]]; // 將基準放到正確位置return i; // 返回基準的索引
}

特點

  • 時間復雜度:平均O(n log n),最壞O(n2)(當數組已經有序時)
  • 空間復雜度:O(log n)(遞歸調用棧)
  • 不穩定排序

適用場景:大規模數據排序,追求性能的情況。

8. 計數排序 - 非比較排序算法

計數排序是一種非比較排序算法,適用于整數排序。它的基本思想是:統計每個元素出現的次數,然后根據統計結果重建有序數組

function countingSort(arr, maxVal) {const count = new Array(maxVal + 1).fill(0); // 統計每個元素出現的次數for (let num of arr) {count[num]++;}const result = [];for (let i = 0; i <= maxVal; i++) {while (count[i]-- > 0) {result.push(i); // 根據統計結果重建數組}}return result;
}

特點

  • 時間復雜度:O(n + k)(k是數據的范圍)
  • 空間復雜度:O(k)
  • 穩定排序(如果實現得當)

適用場景:數據是整數且范圍不大的情況。

9. 桶排序 - 分配到多個桶中排序

桶排序將元素分散到多個"桶"中,每個桶內部進行排序,最后合并所有桶的結果。

function bucketSort(arr, bucketSize = 5) {if (arr.length <= 1) return arr;const min = Math.min(...arr);const max = Math.max(...arr);const bucketCount = Math.floor((max - min) / bucketSize) + 1; // 計算桶的數量const buckets = Array.from({ length: bucketCount }, () => []); // 創建桶// 將元素分配到各個桶中for (let num of arr) {const index = Math.floor((num - min) / bucketSize);buckets[index].push(num);}// 對每個桶進行排序并合并結果return buckets.flatMap(bucket => insertionSort(bucket)); // 這里使用了插入排序
}

特點

  • 時間復雜度:O(n + k),最壞O(n2)(當所有元素都在一個桶中)
  • 空間復雜度:O(n + k)
  • 穩定排序(取決于桶內使用的排序算法)

適用場景:數據分布均勻的情況,特別是浮點數排序。

10. 基數排序 - 按位比較的排序

基數排序是一種非比較排序算法,它按照數字的位數從低位到高位依次排序

function radixSort(arr) {const max = Math.max(...arr);let exp = 1; // 從個位開始while (Math.floor(max / exp) > 0) {countingByDigit(arr, exp); // 按當前位排序exp *= 10; // 移動到下一位}return arr;
}function countingByDigit(arr, exp) {const output = new Array(arr.length).fill(0);const count = new Array(10).fill(0); // 0-9的數字// 統計當前位數字出現的次數for (let num of arr) {count[Math.floor(num / exp) % 10]++;}// 計算累計次數for (let i = 1; i < 10; i++) {count[i] += count[i - 1];}// 從后向前遍歷,保證穩定性for (let i = arr.length - 1; i >= 0; i--) {const digit = Math.floor(arr[i] / exp) % 10;output[--count[digit]] = arr[i];}// 將排序結果復制回原數組for (let i = 0; i < arr.length; i++) {arr[i] = output[i];}
}

特點

  • 時間復雜度:O(nk)(k是最大數字的位數)
  • 空間復雜度:O(n + k)
  • 穩定排序

適用場景:非負整數排序,特別是位數不多的情況。

排序算法性能對比

排序算法最好時間復雜度最壞時間復雜度平均時間復雜度空間復雜度穩定性
冒泡排序O(n)O(n2)O(n2)O(1)?
選擇排序O(n2)O(n2)O(n2)O(1)?
插入排序O(n)O(n2)O(n2)O(1)?
希爾排序O(n log n)O(n2)O(n^1.3)O(1)?
歸并排序O(n log n)O(n log n)O(n log n)O(n)?
堆排序O(n log n)O(n log n)O(n log n)O(1)?
快速排序O(n log n)O(n2)O(n log n)O(log n)?
計數排序O(n + k)O(n + k)O(n + k)O(k)?
桶排序O(n + k)O(n2)O(n + k)O(n + k)?
基數排序O(nk)O(nk)O(nk)O(n + k)?

總結

  1. 簡單排序:冒泡、選擇、插入排序適合小規模數據或學習理解
  2. 高效排序:歸并、堆、快速排序適合大規模數據
  3. 特殊排序:計數、桶、基數排序適合特定場景(整數、浮點數等)

在實際開發中,JavaScript的Array.prototype.sort()方法已經足夠高效,它通常使用快速排序或歸并排序的變體。但理解這些底層算法原理,能幫助我們更好地解決問題和優化代碼。

希望這篇通俗易懂的排序算法指南對你有所幫助!


推薦更多閱讀內容
掌握這些JavaScript技巧,讓你的編碼效率飆升!
JavaScript 字符串字符刪除方法大揭秘
正向代理與反向代理:傻傻分不清楚
JavaScript分鐘轉時間格式(小時+分鐘)的方法及應用
徹底理解 Object.entries() + map():如何把對象轉換成指定格式數組?
深入理解 window.open:用法、參數、兼容性與安全實踐
徹底清除和禁用瀏覽器輸入框歷史記錄的終極指南
JavaScript 開發中的高效技巧與基礎知識解析

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86395.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86395.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86395.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【微信小程序】6、SpringBoot整合WxJava獲取用戶手機號

1、手機號快速驗證組件 手機號快速驗證組件 旨在幫助開發者向用戶發起手機號申請&#xff0c;并且必須經過用戶同意后&#xff0c;開發者才可獲得由平臺驗證后的手機號&#xff0c;進而為用戶提供相應服務。 該能力與手機號實時驗證組件的區別為&#xff1a; 手機號快速驗證…

redis8.0新特性:原生JSON支持詳解

文章目錄 一、寫在前面二、使用1、基本命令&#xff08;1&#xff09;JSON.SET 設置 JSON 值&#xff08;2&#xff09;JSON.GET 獲取 JSON 值&#xff08;3&#xff09;JSON.DEL 刪除 JSON 值&#xff08;4&#xff09;JSON.MGET 批量獲取&#xff08;5&#xff09;JSON.MSET …

QT網絡調試助手開發全指南,軟件設計圖預研,后續文檔跟進補充

網絡調試助手 1 TCP網絡調試助手 1.1 項目概述 網絡相關的一些基礎概念學習QTcpServer 學習QTcpClient 學習TextEdit特定位置輸入文字顏色學習網絡通信相關知識點 復習鞏固之前UI控件 程序運行如下圖所示 1.2 開發流程 1.3 QTtcp 服務器的關鍵流程 工程建立&#xff0c;需要在…

網絡分層模型與協議體系技術研究報告

網絡分層模型是計算機網絡體系結構的核心框架&#xff0c;它通過將復雜的網絡通信過程分解為多個層次&#xff0c;使網絡設計、實現和維護變得更加模塊化和標準化。 一、分層模型概念 1、OSI七層模型的詳細解析 開放系統互連參考模型&#xff08;OSI/RM&#xff09;是國際標…

C++面向對象7——C繼承與C++繼承對比、C++繼承詳解

繼承 C語言與C繼承機制的對比與實現 一、C語言模擬繼承的實現方法 C語言不支持面向對象編程的原生繼承機制&#xff0c;但可以通過結構體嵌套和函數指針組合來模擬。 1. 結構體嵌套實現"is-a"關系 // 基類&#xff1a;Shape typedef struct {int x;int y; } Sha…

運維打鐵: Windows 服務器基礎運維要點解析

文章目錄 思維導圖一級節點&#xff1a;Windows 服務器基礎運維要點 詳細內容解析系統安裝與配置硬件準備安裝介質選擇系統安裝過程初始配置 日常監控與維護性能監控服務狀態檢查日志管理 安全管理賬戶與權限管理防火墻配置病毒防護 備份與恢復備份策略制定備份工具使用恢復測試…

Python實例題:基于量子計算的優化算法實現(量子計算、優化理論)

目錄 Python實例題 題目 問題描述 解題思路 關鍵代碼框架 難點分析 擴展方向 Python實例題 題目 基于量子計算的優化算法實現&#xff08;量子計算、優化理論&#xff09; 問題描述 開發一個基于量子計算的優化算法實現&#xff0c;包含以下功能&#xff1a; 量子計…

基本算法--藍橋杯備考

1.前綴和 1.定義 假設有一個數組a[n],要計算它的前j個元素的和為 a[0]a[1]...a[j-1] 時間復雜度為O(j)&#xff0c;且隨著j的變大時間復雜度越來越大。 使用了前綴和算法則為 sum[j]-sum[j-1] 時間復雜度是O(1)&#xff0c;且數據越大優勢越明顯。 2.例題一 詳解見《可…

pgsql 中各個字符串的區別

PostgreSQL 提供了多種字符串類型&#xff0c;它們在存儲方式、長度限制和適用場景上有所不同。以下是主要字符串類型的詳細對比和區別&#xff1a; 一、核心字符串類型對比 CHAR(n)/CHARACTER(n) 特點&#xff1a;固定長度字符串&#xff0c;不足部分用空格填充最大長度&…

ubuntu中lightdm干嘛的?

在 Ubuntu 或其他 Linux 發行版中&#xff0c;LightDM 是一個輕量級的 顯示管理器&#xff08;Display Manager&#xff09;&#xff0c;負責圖形化登錄界面、用戶認證和會話啟動。以下是它的核心作用、特點及類似替代品的對比&#xff1a; 1. LightDM 的核心作用 功能說明圖形…

GraphQL注入 -- GPN CTF 2025 Real Christmas

part 1 服務器會每段時間禁用已注冊的賬號,此處存在漏洞 def deactivate_user_graphql(email):graphql_endpoint current_app.config["GRAPHQL_ENDPOINT"]query f"""mutation {{deactivateUser (user: {{email: "{email}"}}){{ success…

【機器學習深度學習】非線性激活函數

目錄 前言 一、什么是激活函數&#xff1f; 1.1 作用 二、如果沒有激活函數&#xff0c;會發生什么&#xff1f; 2.1 先看一張圖理解“線性”的局限 2.2 核心認知&#xff1a;為什么非線性如此重要&#xff1f; 三、非線性激活函數到底解決了什么問題&#xff1f; 1. 引…

國外開源客服系統chathoot部署,使用教程

目錄 一、系統版本要求&#xff1a; 二、部署步驟 2.1 安裝docker 和docker-compose 2.2 準備docker-compose.yaml 2.3 初始化數據庫 2.4 安裝nginx 2.6 啟動項目 三、使用教程 一、系統版本要求&#xff1a; linux ubuntu 22.042核4G 40GB&#xff08;或以上&#xf…

什么是回歸測試?什么時候需要做回歸測試?

回歸測試詳解&#xff1a;概念、時機與最佳實踐 1. 什么是回歸測試&#xff1f; 回歸測試&#xff08;Regression Testing&#xff09; 是指在對軟件進行修改&#xff08;如修復Bug、新增功能、優化代碼&#xff09;后&#xff0c;重新執行已有測試用例&#xff0c;以確保&am…

Android-Layout Inspector使用手冊

Layout Inspector Android Layout Inspector 是 Android Studio 中用于調試應用布局的工具 啟動方法&#xff1a; 通過下載Layout Inspector插件&#xff0c;在 “View - Tool Windows - Layout Inspector” 或 “Tools - Layout Inspector” 啟動。 主要界面區域&#xff1a…

postgreSQL 數據庫字典導出工具

為滿足項目驗收文檔需求&#xff0c;開發了一個基于Python的PostgreSQL數據字典導出工具。 廢話不多說&#xff0c;先分享一下 軟件截圖 數據字典文件樣式,文件格式為docx 軟件源碼 基于python開發&#xff0c; import tkinter as tk from tkinter import ttk, messagebox …

【AI解析】 CppNumericalSolvers:一個現代化的 C++17 純頭文件優化庫 示例代碼解析

一個輕量級僅頭文件的 C17 庫&#xff0c;提供針對&#xff08;無&#xff09;約束非線性函數及表達式模板的數值優化方法 https://github.com/PatWie/CppNumericalSolvers CppNumericalSolvers 庫 include 目錄下的文件及其功能說明 根目錄文件 文件名功能說明function.h(主函…

第3篇:Gin的請求處理——獲取客戶端數據(Gin文件上傳,接收JSON數據)

引言&#xff1a;Context是Gin的"瑞士軍刀" 在Gin框架中&#xff0c;Context就像一把多功能的瑞士軍刀&#xff0c;封裝了所有與請求相關的操作。新手開發者常犯的錯誤是只把它當作參數傳遞的工具&#xff0c;卻忽略了它強大的數據處理能力。 想象一個場景&#xf…

啟動hardhat 項目,下載依賴的npm問題

Windows 環境 Hardhat 依賴安裝問題排查指南 &#x1f6a8; 問題描述 在 Windows 環境下安裝 Hardhat 項目依賴時&#xff0c;遇到以下錯誤&#xff1a; npm ERR! code ETARGET npm ERR! notarget No matching version found for nomicfoundation/edr^0.11.1. npm ERR! nota…

大數據里的拉鏈表:數據版本管理的時間膠囊

哈嘍各位數據打工人&#xff5e;今天咱們來聊聊大數據領域一個超實用的神器 ——拉鏈表&#xff01;聽起來像時尚單品&#xff1f;NoNoNo&#xff0c;它可是數據倉庫里管理歷史數據的寶藏工具? 就算你是剛入門的小白也能輕松聽懂&#xff0c;咱們全程少玩比喻多講人話&#xf…