深入淺析JavaScript中的快速排序
介紹
排序是指以特定順序(數字或字母)排列線性表的元素。排序通常與搜尋一起配合使用。
有許多排序演算法,而迄今為止最快的演算法之一是快速排序(Quicksort)。
快速排序用分治策略對給定的列表元素進行排序。這意味著演算法將問題分解為子問題,直到子問題變得足夠簡單可以直接解決為止。
從演算法上講,這可以用遞歸或循環實現。但是對於這個問題,用遞歸法更為自然。
了解快速排序背後的邏輯
先看一下快速排序的工作原理:
- 在陣列中選擇一個元素,這個元素稱為基準(Pivot)。通常把數組中的第一個或最後一個元素當作基準。
- 然後,重新排列陣列的元素,以使基準左側的有元素都小於基準,而右側的所有元素都大於基準。這一步稱為分區。如果一個元素等於基準,那麼在哪一邊都無關緊要。
- 針對基準的左側和右側分別重複此過程,直到陣列完成排序。
接下來透過一個例子來理解這些步驟。假設有一個含有未排序元素 [7, -2, 4, 1, 6, 5, 0, -4, 2]
的陣列。選擇最後一個元素作為基準。陣列的分解步驟如下圖所示:
在演算法的步驟1中被選為基準的元素帶顏色。分區後,基準元素始終處於陣列中的正確位置。
黑色粗體邊框的陣列表示該特定遞歸分支結束時的樣子,最後得到的陣列只包含一個元素。
最後可以看到演算法的結果排序。
用 JavaScript 實作快速排序
這個演算法的主幹是「分割」步驟。無論用遞歸或循環的方法,這個步驟都是一樣的。
正是因為這個特點,首先寫為數組分區的程式碼partition()
:
function partition(arr, start, end){ // 以最后一个元素为基准 const pivotValue = arr[end]; let pivotIndex = start; for (let i = start; i < end; i++) { if (arr[i] < pivotValue) { // 交换元素 [arr[i], arr[pivotIndex]] = [arr[pivotIndex], arr[i]]; // 移动到下一个元素 pivotIndex++; } } // 把基准值放在中间 [arr[pivotIndex], arr[end]] = [arr[end], arr[pivotIndex]] return pivotIndex; };
程式碼以最後一個元素為基準,用變數pivotIndex
來追蹤「中間」位置,這個位置左邊的所有元素都比pivotValue
小,而右邊的元素都比pivotValue
大。
最後一步把基準(最後一個元素)與 pivotIndex
交換。
遞歸實作
在實作了partition()
函數之後,我們必須遞歸地解決這個問題,並應用分區邏輯以完成其餘步驟:
function quickSortRecursive(arr, start, end) { // 终止条件 if (start >= end) { return; } // 返回 pivotIndex let index = partition(arr, start, end); // 将相同的逻辑递归地用于左右子数组 quickSort(arr, start, index - 1); quickSort(arr, index + 1, end); }
在這個函數中先對數組進行分區,之後再對左右兩個子數組進行分區。只要這個函數收到一個不為空或有多個元素的數組,則會重複此過程。
空數組和僅包含一個元素的陣列被視為已排序。
最後用下面的例子來測試:
array = [7, -2, 4, 1, 6, 5, 0, -4, 2] quickSortRecursive(array, 0, array.length - 1) console.log(array)
輸出:
-4,-2,0,1,2,4,5,6,7
循環實作
快速排序的遞歸方法更直觀。但是用迴圈實現快速排序是一個相對常見的面試題。
與大多數的遞歸到循環的轉換方案一樣,最先想到的是用堆疊來模擬遞歸呼叫。這樣做可以重複使用一些我們熟悉的遞歸邏輯,並在循環中使用。
我們需要一種追蹤剩下的未排序子數組的方法。一種方法是簡單地把「成對」的元素保留在堆疊中,用來表示給定未排序子陣列的 start
和 end
。
JavaScript 沒有明確的堆疊資料結構,但是陣列支援 push()
和 pop()
函數。但不支援 peek()
函數,所以必須用 stack [stack.length-1]
手動檢查堆疊頂部。
我們將使用與遞歸方法相同的「分區」功能。看看如何寫Quicksort部分:
function quickSortIterative(arr) { // 用push()和pop()函数创建一个将作为栈使用的数组 stack = []; // 将整个初始数组做为“未排序的子数组” stack.push(0); stack.push(arr.length - 1); // 没有显式的peek()函数 // 只要存在未排序的子数组,就重复循环 while(stack[stack.length - 1] >= 0){ // 提取顶部未排序的子数组 end = stack.pop(); start = stack.pop(); pivotIndex = partition(arr, start, end); // 如果基准的左侧有未排序的元素, // 则将该子数组添加到栈中,以便稍后对其进行排序 if (pivotIndex - 1 > start){ stack.push(start); stack.push(pivotIndex - 1); } // 如果基准的右侧有未排序的元素, // 则将该子数组添加到栈中,以便稍后对其进行排序 if (pivotIndex + 1 < end){ stack.push(pivotIndex + 1); stack.push(end); } } }
以下是測試程式碼:
ourArray = [7, -2, 4, 1, 6, 5, 0, -4, 2] quickSortIterative(ourArray) console.log(ourArray)
#輸出:
-4,-2,0,1,2,4,5,6,7
視覺化演示
當涉及排序演算法時,將其視覺化能幫我們直觀的了解它們是如何運作的,下面這個例子搬運自維基百科:
在圖中也把最後一個元素作為基準。給定數組分區後,遞歸遍歷左側,直到將其完全排序為止。然後對右側進行排序。
快速排序的效率
現在討論它的時間和空間複雜度。快速排序在最壞情況下的時間複雜度是 $O(n^2)$。平均時間複雜度為 $O(n\log n)$。通常,使用隨機版本的快速排序可以避免最壞的情況。
快速排序演算法的弱點是基準的選擇。每選擇一次錯誤的基準(大於或小於大多數元素的基準)都會帶來最壞的時間複雜度。重複選擇基準時,如果元素值小於或大於該元素的基準時,時間複雜度為 $O(n\log n)$。
根據經驗可以觀察到,無論採用哪一種資料基準選擇策略,快速排序的時間複雜度都傾向於具有 $O(n\log n)$ 。
快速排序不會佔用任何額外的空間(不包括為遞歸呼叫保留的空間)。這種演算法稱為in-place演算法,不需要額外的空間。
更多程式相關知識,請造訪:程式設計入門! !
以上是深入淺析JavaScript中的快速排序的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

寫在前面&筆者的個人理解目前,在整個自動駕駛系統當中,感知模組扮演了其中至關重要的角色,行駛在道路上的自動駕駛車輛只有通過感知模組獲得到準確的感知結果後,才能讓自動駕駛系統中的下游規控模組做出及時、正確的判斷和行為決策。目前,具備自動駕駛功能的汽車中通常會配備包括環視相機感測器、光達感測器以及毫米波雷達感測器在內的多種數據資訊感測器來收集不同模態的信息,用於實現準確的感知任務。基於純視覺的BEV感知演算法因其較低的硬體成本和易於部署的特點,以及其輸出結果能便捷地應用於各種下游任務,因此受到工業

C++中機器學習演算法面臨的常見挑戰包括記憶體管理、多執行緒、效能最佳化和可維護性。解決方案包括使用智慧指標、現代線程庫、SIMD指令和第三方庫,並遵循程式碼風格指南和使用自動化工具。實作案例展示如何利用Eigen函式庫實現線性迴歸演算法,有效地管理記憶體和使用高效能矩陣操作。

C++sort函數底層採用歸併排序,其複雜度為O(nlogn),並提供不同的排序演算法選擇,包括快速排序、堆排序和穩定排序。

人工智慧(AI)與執法領域的融合為犯罪預防和偵查開啟了新的可能性。人工智慧的預測能力被廣泛應用於CrimeGPT(犯罪預測技術)等系統,用於預測犯罪活動。本文探討了人工智慧在犯罪預測領域的潛力、目前的應用情況、所面臨的挑戰以及相關技術可能帶來的道德影響。人工智慧和犯罪預測:基礎知識CrimeGPT利用機器學習演算法來分析大量資料集,識別可以預測犯罪可能發生的地點和時間的模式。這些資料集包括歷史犯罪統計資料、人口統計資料、經濟指標、天氣模式等。透過識別人類分析師可能忽視的趨勢,人工智慧可以為執法機構

PHP與Vue:完美搭檔的前端開發利器在當今網路快速發展的時代,前端開發變得愈發重要。隨著使用者對網站和應用的體驗要求越來越高,前端開發人員需要使用更有效率和靈活的工具來創建響應式和互動式的介面。 PHP和Vue.js作為前端開發領域的兩個重要技術,搭配起來可以稱得上是完美的利器。本文將探討PHP和Vue的結合,以及詳細的程式碼範例,幫助讀者更好地理解和應用這兩

01前景概要目前,難以在檢測效率和檢測結果之間取得適當的平衡。我們研究了一種用於高解析度光學遙感影像中目標偵測的增強YOLOv5演算法,利用多層特徵金字塔、多重偵測頭策略和混合注意力模組來提高光學遙感影像的目標偵測網路的效果。根據SIMD資料集,新演算法的mAP比YOLOv5好2.2%,比YOLOX好8.48%,在偵測結果和速度之間達到了更好的平衡。 02背景&動機隨著遠感技術的快速發展,高解析度光學遠感影像已被用於描述地球表面的許多物體,包括飛機、汽車、建築物等。目標檢測在遠感影像的解釋中

在前端開發面試中,常見問題涵蓋廣泛,包括HTML/CSS基礎、JavaScript基礎、框架和函式庫、專案經驗、演算法和資料結構、效能最佳化、跨域請求、前端工程化、設計模式以及新技術和趨勢。面試官的問題旨在評估候選人的技術技能、專案經驗以及對行業趨勢的理解。因此,應試者應充分準備這些方面,以展現自己的能力和專業知識。

一、58畫像平台建置背景首先和大家分享下58畫像平台的建造背景。 1.傳統的畫像平台傳統的想法已經不夠,建立用戶畫像平台依賴數據倉儲建模能力,整合多業務線數據,建構準確的用戶畫像;還需要數據挖掘,理解用戶行為、興趣和需求,提供演算法側的能力;最後,還需要具備數據平台能力,有效率地儲存、查詢和共享用戶畫像數據,提供畫像服務。業務自建畫像平台和中台類型畫像平台主要區別在於,業務自建畫像平台服務單條業務線,按需定制;中台平台服務多條業務線,建模複雜,提供更為通用的能力。 2.58中台畫像建構的背景58的使用者畫像
