排序算法之高效排序:快速排序,歸并排序,堆排序詳解

排序算法之高效排序:快速排序、歸并排序、堆排序詳解

  • 前言
  • 一、快速排序(Quick Sort)
    • 1.1 算法原理
    • 1.2 代碼實現(Python)
    • 1.3 性能分析
  • 二、歸并排序(Merge Sort)
    • 2.1 算法原理
    • 2.2 代碼實現(Java)
    • 2.3 性能分析
  • 三、堆排序(Heap Sort)
    • 3.1 算法原理
    • 3.2 代碼實現(C++)
    • 3.3 性能分析
  • 四、三種高效排序算法的對比與適用場景
  • 總結

前言

相較于上一期我講的冒泡、選擇、插入等基礎排序,快速排序、歸并排序和堆排序憑借更優的時間復雜度,成為處理大規模數據排序任務的首選方案。本文我將深入剖析這三種高效排序算法的原理、實現細節、性能特點及適用場景,助力你掌握它們在實際開發中的應用技巧。

一、快速排序(Quick Sort)

1.1 算法原理

快速排序由托尼?霍爾(Tony Hoare)于 1959 年提出,是一種基于分治思想的排序算法。其核心步驟如下:

選擇基準值:從數組中選取一個元素作為基準值(通常選擇第一個、最后一個或中間元素)。

分區操作:將數組分為兩個子數組,使得左邊子數組的所有元素都小于等于基準值,右邊子數組的所有元素都大于基準值。

遞歸排序:對左右兩個子數組分別遞歸地進行快速排序。

通過不斷重復上述步驟,最終使整個數組達到有序狀態。例如,對于數組[5, 3, 8, 6, 2],若選擇5作為基準值,經過分區操作后,數組變為[3, 2, 5, 6, 8],然后分別對[3, 2][6, 8]進行遞歸排序,最終得到有序數組[2, 3, 5, 6, 8]
00

1.2 代碼實現(Python)

def quick_sort(arr):if len(arr) <= 1:return arrpivot = arr[len(arr) // 2]left = [x for x in arr if x < pivot]middle = [x for x in arr if x == pivot]right = [x for x in arr if x > pivot]return quick_sort(left) + middle + quick_sort(right)

上述代碼中,首先判斷數組長度,若小于等于 1 則直接返回。接著選取基準值,通過列表推導式將數組分為小于、等于、大于基準值的三個部分,最后遞歸地對左右子數組進行排序并合并。

1.3 性能分析

時間復雜度

平均情況下,快速排序的時間復雜度為 O ( n log ? n ) O(n \log n) O(nlogn) ,其中n為數組元素個數。

最壞情況下(如數組已有序且每次選擇的基準值為最大或最小元素),時間復雜度退化為 O ( n 2 ) O(n^2) O(n2)

空間復雜度:快速排序的空間復雜度主要取決于遞歸調用棧的深度。平均情況下,空間復雜度為 O ( log ? n ) O(\log n) O(logn) ;在最壞情況下,遞歸深度達到n,空間復雜度為 O ( n ) O(n) O(n)

穩定性:快速排序是不穩定的排序算法,因為在分區過程中,相同元素的相對順序可能會發生改變。

二、歸并排序(Merge Sort)

2.1 算法原理

歸并排序同樣基于分治思想,它將一個數組分成兩個大致相等的子數組,分別對兩個子數組進行排序,然后將排好序的子數組合并成一個最終的有序數組。具體步驟如下:

分解:將待排序數組不斷平均分成兩個子數組,直到子數組長度為 1(單個元素可視為有序)。

排序:對每個子數組進行排序(可使用其他排序方法,通常也是遞歸地使用歸并排序)。

合并:從最底層開始,將兩個有序的子數組合并成一個更大的有序數組,不斷向上合并,直至得到整個有序數組。

例如,對于數組[8, 4, 2, 1, 7, 6, 3, 5],先分解為多個子數組,再依次排序并合并,最終得到有序數組[1, 2, 3, 4, 5, 6, 7, 8]
01

2.2 代碼實現(Java)

import java.util.Arrays;public class MergeSort {public static void mergeSort(int[] arr) {if (arr == null) {return;}int[] temp = new int[arr.length];mergeSort(arr, temp, 0, arr.length - 1);}private static void mergeSort(int[] arr, int[] temp, int left, int right) {if (left < right) {int mid = left + (right - left) / 2;mergeSort(arr, temp, left, mid);mergeSort(arr, temp, mid + 1, right);merge(arr, temp, left, mid, right);}}private static void merge(int[] arr, int[] temp, int left, int mid, int right) {System.arraycopy(arr, left, temp, left, right - left + 1);int i = left;int j = mid + 1;int k = left;while (i <= mid && j <= right) {if (temp[i] <= temp[j]) {arr[k++] = temp[i++];} else {arr[k++] = temp[j++];}}while (i <= mid) {arr[k++] = temp[i++];}while (j <= right) {arr[k++] = temp[j++];}}public static void main(String[] args) {int[] arr = {8, 4, 2, 1, 7, 6, 3, 5};mergeSort(arr);System.out.println(Arrays.toString(arr));}
}

在上述 Java 代碼中,mergeSort方法作為入口,調用遞歸的mergeSort方法進行分解和排序,merge方法用于合并兩個有序子數組。通過臨時數組temp輔助完成合并操作,保證合并過程中數據的正確處理。

2.3 性能分析

時間復雜度:歸并排序無論在最好、最壞還是平均情況下,時間復雜度均為 O ( n log ? n ) O(n \log n) O(nlogn) ,因為每次分解和合并操作的時間開銷相對固定,總操作次數與 n log ? n n \log n nlogn相關。

空間復雜度:歸并排序在合并過程中需要使用額外的空間存儲臨時數據,空間復雜度為 O ( n ) O(n) O(n)

穩定性:歸并排序是穩定的排序算法,在合并子數組時,相同元素的相對順序不會發生改變。

三、堆排序(Heap Sort)

3.1 算法原理

堆排序利用了堆這種數據結構(大頂堆或小頂堆)的特性來實現排序。大頂堆的特點是每個父節點的值都大于或等于其子節點的值,小頂堆則相反。堆排序的主要步驟如下:

建堆:將待排序數組構建成一個大頂堆(升序排序時)或小頂堆(降序排序時)。

交換與調整:將堆頂元素(最大值或最小值)與堆的最后一個元素交換,然后對剩余元素重新調整堆結構,使其再次滿足堆的性質。

重復操作:不斷重復步驟 2,直到堆中只剩下一個元素,此時數組即為有序狀態。

例如,對于數組[4, 6, 8, 5, 9],先構建大頂堆[9, 6, 8, 5, 4],然后將 9 與 4 交換,調整堆為[8, 6, 4, 5, 9],依次類推,最終得到有序數組[4, 5, 6, 8, 9]
02

3.2 代碼實現(C++)

#include <iostream>
#include <vector>
using namespace std;// 調整堆結構
void heapify(vector<int>& arr, int n, int i) {int largest = i;int left = 2 * i + 1;int right = 2 * i + 2;if (left < n && arr[left] > arr[largest]) {largest = left;}if (right < n && arr[right] > arr[largest]) {largest = right;}if (largest != i) {swap(arr[i], arr[largest]);heapify(arr, n, largest);}
}// 堆排序
void heapSort(vector<int>& arr) {int n = arr.size();// 建堆for (int i = n / 2 - 1; i >= 0; --i) {heapify(arr, n, i);}// 交換與調整for (int i = n - 1; i > 0; --i) {swap(arr[0], arr[i]);heapify(arr, i, 0);}
}

上述 C++ 代碼中,heapify函數用于調整堆結構,確保以i為根節點的子樹滿足堆的性質。heapSort函數先進行建堆操作,然后通過不斷交換堆頂元素和堆的最后一個元素,并調整堆結構,實現排序功能。

3.3 性能分析

時間復雜度:堆排序的時間復雜度主要由建堆和調整堆兩部分組成。建堆的時間復雜度為 O ( n ) O(n) O(n) ,調整堆的時間復雜度為 O ( n log ? n ) O(n \log n) O(nlogn) ,因此整體時間復雜度為 O ( n log ? n ) O(n \log n) O(nlogn) ,且在最好、最壞和平均情況下均保持不變。

空間復雜度:堆排序在排序過程中只需要常數級別的額外空間,空間復雜度為 O ( 1 ) O(1) O(1)

穩定性:堆排序是不穩定的排序算法,因為在調整堆結構時,相同元素的相對順序可能會被打亂。

四、三種高效排序算法的對比與適用場景

排序算法平均時間復雜度最壞時間復雜度空間復雜度穩定性適用場景
快速排序 O ( n log ? n ) O(n \log n) O(nlogn) O ( n 2 ) O(n^2) O(n2) O ( log ? n ) O(\log n) O(logn)不穩定數據隨機分布、對空間要求不高的場景;適合內部排序,常用于通用排序庫
歸并排序 O ( n log ? n ) O(n \log n) O(nlogn) O ( n log ? n ) O(n \log n) O(nlogn) O ( n ) O(n) O(n)穩定對穩定性有要求、外部排序(如處理大文件)、數據規模較大且內存充足的場景
堆排序 O ( n log ? n ) O(n \log n) O(nlogn) O ( n log ? n ) O(n \log n) O(nlogn) O ( 1 ) O(1) O(1)不穩定對空間要求嚴格、需要在線性時間內找到最大 / 最小元素的場景,如優先隊列實現

總結

快速排序、歸并排序和堆排序作為高效排序算法,在不同的應用場景中發揮著各自的優勢。快速排序憑借其簡潔高效的特點,在多數常規排序任務中表現出色;歸并排序以穩定的性能和適用于外部排序的特性,成為處理大規模數據的可靠選擇;堆排序則因其對空間的高效利用和穩定的時間復雜度,在特定場景下展現出獨特價值。下期博客中,我將帶你探索更多高級排序算法與優化技巧,例如希爾排序、計數排序等,分析它們與快速排序、歸并排序、堆排序的差異,以及在不同業務場景中的實際應用案例,幫助大家進一步拓寬排序算法的知識邊界。

That’s all, thanks for reading!
創作不易,點贊鼓勵;
知識無價,收藏備用;
持續精彩,關注不錯過!

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

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

相關文章

Android開發——輪播圖引入

Android開發——輪播圖引入 一、前期準備與依賴引入二、配置啟動類(AndroidManifest.xml)三、構造啟動類(MainActivity.java)四、配置布局文件(activity_main.xml)五、最終效果與擴展方向一、前期準備與依賴引入 在開始引入輪播圖功能前,需確保已正確搭建Android開發環境…

[逆向工程]C++實現DLL卸載(二十六)

[逆向工程]C實現DLL卸載&#xff08;二十六&#xff09; 引言 DLL注入&#xff08;DLL Injection&#xff09;是Windows系統下實現進程間通信、功能擴展、監控調試的核心技術之一。本文將從原理分析、代碼實現、實戰調試到防御方案&#xff0c;全方位講解如何用C實現DLL注入&…

lesson01-PyTorch初見(理論+代碼實戰)

一、初識PyTorch 二、同類框架 PyTorchVSTensorFlow 三、參數 對比 四、PyTorch生態 四、常用的網絡層 五、代碼分析 import torch from torch import autogradx torch.tensor(1.) a torch.tensor(1., requires_gradTrue) b torch.tensor(2., requires_gradTrue) c tor…

STM32中的DMA

DMA介紹 什么是DMA? DMA&#xff08;Direct Memory Access&#xff0c;直接存儲器訪問&#xff09;提供在外設與內存、存儲器和存儲器之間的高速數據傳輸使用。它允許不同速度的硬件裝置來溝通&#xff0c;而不需要依賴于CPU&#xff0c;在這個時間中&#xff0c;CPU對于內存…

聊聊JetCache的緩存構建

序 本文主要研究一下JetCache的緩存構建 invokeWithCached com/alicp/jetcache/anno/method/CacheHandler.java private static Object invokeWithCached(CacheInvokeContext context)throws Throwable {CacheInvokeConfig cic context.getCacheInvokeConfig();CachedAnnoC…

c#隊列及其操作

可以用數組、鏈表實現隊列&#xff0c;大致與棧相似&#xff0c;簡要介紹下隊列實現吧。值得注意的是循環隊列判空判滿操作&#xff0c;在用鏈表實現時需要額外思考下出入隊列條件。 設計頭文件 #ifndef ARRAY_QUEUE_H #define ARRAY_QUEUE_H#include <stdbool.h> #incl…

開源項目實戰學習之YOLO11:12.3 ultralytics-models-sam-encoders.py源碼分析

?? 點擊關注不迷路 ?? 點擊關注不迷路 ?? 另外,前些天發現了一個巨牛的AI人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。感興趣的可以點擊相關跳轉鏈接。 點擊跳轉到網站。 ultralytics-models-sam 1.sam-modules-encoders.pyblocks.py: 定義模型中的各…

STM32 | FreeRTOS 消息隊列

01 一、概述 隊列又稱消息隊列&#xff0c;是一種常用于任務間通信的數據結構&#xff0c;隊列可以在任務與任務間、中斷和任務間傳遞信息&#xff0c;實現了任務接收來自其他任務或中斷的不固定長度的消息&#xff0c;任務能夠從隊列里面讀取消息&#xff0c;當隊列中的消…

Java 安全漏洞掃描工具:如何快速發現和修復潛在問題?

Java 安全漏洞掃描工具&#xff1a;如何快速發現和修復潛在問題&#xff1f; 在當今的軟件開發領域&#xff0c;Java 作為一種廣泛使用的編程語言&#xff0c;其應用的規模和復雜度不斷攀升。然而&#xff0c;隨著應用的拓展&#xff0c;Java 應用面臨的潛在安全漏洞風險也日益…

Python繪制克利夫蘭點圖:從入門到實戰

Python繪制克利夫蘭點圖&#xff1a;從入門到實戰 引言 克利夫蘭點圖&#xff08;Cleveland Dot Plot&#xff09;是一種強大的數據可視化工具&#xff0c;由統計學家William Cleveland在1984年提出。這種圖表特別適合展示多個類別的數值比較&#xff0c;比傳統的條形圖更直觀…

LVGL- Calendar 日歷控件

1 日歷控件 1.1 日歷背景 lv_calendar 是 LVGL&#xff08;Light and Versatile Graphics Library&#xff09;提供的標準 GUI 控件之一&#xff0c;用于顯示日歷視圖。它支持用戶查看某年某月的完整日歷&#xff0c;還可以實現點擊日期、標記日期、導航月份等操作。這個控件…

多指標組合策略

該策略(MultiConditionStrategy)是一種基于多種技術指標和市場條件的交易策略。它通過綜合考慮多個條件來生成交易信號,從而決定買入或賣出的時機。 以下是對該策略的詳細分析: 交易邏輯思路 1. 條件1:星期幾和價格變化判斷 - 該條件根據當前日期是星期幾以及價格的變化…

BC 范式與 4NF

接下來我們詳細解釋 BC 范式&#xff08;Boyce-Codd范式&#xff0c;簡稱 BCNF&#xff09;&#xff0c;并通過具體例子說明其定義和應用。 一、BC范式的定義 BC范式&#xff08;Boyce-Codd范式&#xff0c;BCNF&#xff09;是數據庫規范化理論中的一種范式&#xff0c;它比第…

基于 CSS Grid 的網頁,拆解頁面整體布局結構

通過以下示例拆解網頁整體布局結構&#xff1a; 一、基礎結構&#xff08;HTML骨架&#xff09; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

采購流程規范化如何實現?日事清流程自動化助力需求、采購、財務高效協作

采購審批流程全靠人推進&#xff0c;內耗嚴重&#xff0c;效率低下&#xff1f; 花重金上了OA&#xff0c;結果功能有局限、不靈活&#xff1f; 問題出在哪里&#xff1f;是我們的要求太多、太苛刻嗎&#xff1f;NO&#xff01; 流程名稱&#xff1a; 采購審批管理 流程功能…

全棧項目搭建指南:Nuxt.js + Node.js + MongoDB

全棧項目搭建指南&#xff1a;Nuxt.js Node.js MongoDB 一、項目概述 我們將構建一個完整的全棧應用&#xff0c;包含&#xff1a; 前端&#xff1a;Nuxt.js (SSR渲染)后端&#xff1a;Node.js (Express/Koa框架)數據庫&#xff1a;MongoDB后臺管理系統&#xff1a;集成在同…

NVMe簡介6之PCIe事務層

PCIe的事務層連接了PCIe設備核心與PCIe鏈路&#xff0c;這里主要基于PCIe事務層進行分析。事務層采用TLP傳輸事務&#xff0c;完整的TLP由TLPPrefix、TLP頭、Payload和TLP Digest組成。TLP頭是TLP中最關鍵的部分&#xff0c;一般由三個或四個雙字的長度&#xff0c;其格式定義如…

Python異常模塊和包

異常 當檢測到一個錯誤時&#xff0c;Python解釋器就無法繼續執行了&#xff0c;反而出現了一些錯誤的提示&#xff0c;這就是所謂的“異常”, 也就是我們常說的BUG 例如&#xff1a;以r方式打開一個不存在的文件。 f open(‘python1.txt’,‘r’,encoding‘utf-8’) 當我們…

匯編:循環程序設計

一、 實驗要求 熟練掌握循環程序設計的基本方法熟練掌握單片機外部存儲空間的訪問方法 二、 實驗設計 1.整體思路 先初始化一些寄存器和數據存儲位置&#xff0c;然后調用兩個子程序Procedure1和Procedure2&#xff0c;分別從SRC復制數據到DEST&#xff0c;一個從開頭到末尾&…

典籍知識問答模塊AI問答bug修改

一、修改流式數據處理問題 1.問題描述&#xff1a;由于傳來的數據形式如下&#xff1a; event:START data:350 data:< data:t data:h data:i data:n data:k data:> data: data: data: data: data:嗯 data:&#xff0c; 導致需要修改獲取正常的當前信息id并更…