歸并排序詳解:優雅的分治藝術

什么?歸并排序?這讓博主想起了大學那會被《數據結構與算法》支配的恐懼…
哈哈言歸正傳,一直想對算法做一個專欄,因為其實工作中很少很少有機會用到算法,倒是很多工具方法底層會使用,工作被各種需求業務“折磨”的讓大家很少再花費精力去深入這些底層,更別說很少有人天天刷算法,導致以前在學校為了應付面試和筆試(也包括社招)學習的這些玩意和基礎隨著時間慢慢消磨殆盡,所以這篇專欄來了!每一篇博主都會詳細說明這些算法的設計思想和代碼實現,幫助博主和大家共同進步,每篇如果有錯誤和需要改進的地方歡迎大家提出!下面開始我們第一篇,也是基礎的——歸并排序。

文章目錄

  • 前言
  • 🌟 核心思想
  • ?? Java實現
  • 🔍 時間復雜度分析
  • 📦 空間復雜度
  • ? 算法特性
  • ? 優化技巧
  • 💡 實際應用場景
  • 🌐 總結


前言

歸并排序是一種基于分治法的高效排序算法,由馮·諾依曼于1945年首次提出。它以其穩定的O(n log n)時間復雜度和優雅的實現方式在算法領域占據重要地位。下面我將詳細介紹歸并排序的原理,并提供完整的Java實現。


🌟 核心思想

先給出歸并排序示意圖:
歸并排序

歸并排序的核心思想可概括為三步:

  1. 分解:將待排序數組遞歸地分成兩個子數組。
  2. 解決:對子數組進行排序(遞歸排序)。
  3. 合并:將兩個有序子數組合并成一個有序數組。
原始數組:[38, 27, 43, 3, 9, 82, 10]分解過程:
1. [38, 27, 43, 3]  [9, 82, 10]
2. [38, 27] [43, 3]  [9, 82] [10]
3. [38] [27] [43] [3] [9] [82] [10]合并過程:
1. [27, 38] [3, 43]  [9, 82] [10]
2. [3, 27, 38, 43]    [9, 10, 82]
最終結果:[3, 9, 10, 27, 38, 43, 82]

?? Java實現

public class MergeSort {// 歸并排序主方法public static void mergeSort(int[] arr) {if (arr == null || arr.length <= 1) {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 = {38, 27, 43, 3, 9, 82, 10};System.out.println("排序前: " + Arrays.toString(arr));mergeSort(arr);System.out.println("排序后: " + Arrays.toString(arr));}
}

算法解析

  1. mergeSort()方法:
    • 公共入口方法。
    • 創建臨時數組避免在遞歸過程中重復創建。
    • 調用私有遞歸方法。
  2. 遞歸排序:
    • 計算中點:mid = left + (right - left)/2(避免整數溢出)。
    • 遞歸排序左半部分:left到mid。
    • 遞歸排序右半部分:mid+1到right。
    • 合并兩個有序子數組。
  3. merge()方法:
    • 復制當前段數據到臨時數組。
    • 使用三個指針分別追蹤:
      • i:左子數組當前位置
      • j:右子數組當前位置
      • k:合并后數組當前位置
    • 比較左右子數組元素,將較小者放入原數組。
    • 處理剩余元素。

🔍 時間復雜度分析

操作時間復雜度
分解O(log n)
合并O(n)
總復雜度O(n log n)

歸并排序在任何情況下的時間復雜度都是穩定的O(n log n),這是它的最大優勢。

📦 空間復雜度

  • O(n):需要額外的空間存儲臨時數組
  • 不是原地排序算法(in-place)

? 算法特性

  1. 穩定性:保持相等元素的原始順序(關鍵:合并時left[i] <= right[j])
  2. 適用場景:大數據量排序、鏈表排序、外部排序
  3. 不適用場景:內存受限的小數據量排序

? 優化技巧

  1. 小數組切換插入排序:當子數組長度較小時使用插入排序。

歸并排序即使處理小規模數據仍需遞歸調用、分割合并等操作,其時間復雜度雖為O(n log n),但?遞歸棧管理、函數調用等額外開銷的常數因子較大?,插入排序在小規模數據(如n≤15)時實際運行時間接近O(n)。數據量≤15時,元素局部有序概率高,實際操作次數接近線性,且其?簡單比較交換操作的常數因子極低?,在閾值范圍內性能顯著優于歸并排序。
ex: 常數因子指算法時間復雜度表示式中的固定系數(如O(2n)中的2),簡單交換位移不需要遞歸方法調用也不需要分配空間,自然開銷低。

private static final int INSERTION_THRESHOLD = 15;private static void mergeSort(int[] arr, int[] temp, int left, int right) {if (right - left <= INSERTION_THRESHOLD) {insertionSort(arr, left, right);return;}// 其余代碼不變
}private static void insertionSort(int[] arr, int left, int right) {for (int i = left + 1; i <= right; i++) {int key = arr[i];int j = i - 1;while (j >= left && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}
}
  1. 邊界檢查優化:在合并前檢查左子數組最大值是否小于等于右子數組最小值。
// 在merge方法開始處添加
if (arr[mid] <= arr[mid + 1]) {return; // 已經有序,無需合并
}
  1. 避免重復復制:通過交換輸入數組和臨時數組的角色減少復制操作

💡 實際應用場景

  1. 大數據處理:MapReduce等分布式計算框架的底層排序
  2. 外部排序:處理超過內存容量的數據集
  3. 穩定排序需求:數據庫排序、訂單記錄處理
  4. 鏈表排序:歸并排序是鏈表最高效的排序方式(鏈表歸并排序僅需修改節點指針指向,實現?O(1)空間復雜度?的原址排序)

🌐 總結

歸并排序以其穩定高效的特性,成為處理大規模數據的首選算法之一。雖然需要額外空間,但它的分治思想在算法設計中具有重要地位,是理解遞歸和分治策略的經典案例。

學過Java的小伙伴,Arrays.sort()Collections.sort()?底層就是使用優化后的歸并排序呦,感興趣的小伙伴可以再深入研究下。

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

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

相關文章

新零售視域下實體與虛擬店融合的技術邏輯與商業模式創新——基于開源AI智能名片與鏈動2+1模式的S2B2C生態構建

摘要&#xff1a;新零售的核心在于打破線上線下邊界&#xff0c;構建“人、貨、場”的全場景融合生態。本文提出&#xff0c;實體線下店與虛擬店的協同發展是新零售的重要演進方向&#xff0c;其底層邏輯在于滿足消費者作為“現實人”的體驗需求與“虛擬人”的效率需求。通過引…

可視化圖解算法51:尋找第K大(數組中的第K個最大的元素)

牛客網 面試筆試 TOP101 | LeetCode 215. 數組中的第K個最大元素 1. 題目 描述 有一個整數數組&#xff0c;請你找出數組中第 k 大的數。 給定一個整數數組 a ,同時給定它的大小n和要找的 k &#xff0c;請返回第 k 大的數(包括重復的元素&#xff0c;不用去重)&…

DataWhale-零基礎網絡爬蟲技術(一)

課程鏈接先給各位 ↓↓↓ &#xff08;點擊即可食用.QAQ Datawhale-學用 AI,從此開始 一、引言 還是在筆記的開始&#xff0c;嘮嘮一些自己的故事 十年前第一次接觸網絡&#xff0c;也可以說是第一次接觸計算機的時候&#xff0c;那時候還是在中學階段&#xff0c;那時候大…

Linux02

目錄 linux常用命令 用戶和權限 壓縮和解壓縮 其他相關命令 Linux中安裝常用軟件 1.1. jdk的安裝 1.1.1. 卸載linux中自帶的open-jdk 1.1.2. 把安裝包上傳到 linux上 1.1.3. 解壓安裝包 1.1.4. 配置環境變量 1.1.5 驗證環境變量 1.3 安裝mysql 1.3.1. 檢查依賴 1.…

JavaSE超詳細筆記-網絡編程篇-基于黑馬

1. 什么是網絡編程【理解】 1.1 概念 在網絡通信協議下&#xff0c;不同計算機上運行的程序&#xff0c;進行的數據傳輸。 應用場景: 即時通信、網游對戰、金融證券、國際貿易、郵件、等等。 不管是什么場景&#xff0c;都是計算機跟計算機之間通過網絡進行數據傳輸Java中可以使…

時序數據庫Influxdb3 core安裝

本文介紹時序數據庫Influxdb3 core(開源版本)的安裝和簡單使用以及調優參數的介紹。 預期&#xff1a; 安裝時序數據庫Influxdb3 core 創建數據庫mydb 寫入數據&#xff1b; 使用influxdb3-cli 和 grafana2種方式查詢寫入的數據 前期準備&#xff1a; linux服務器(本文服…

區間合并:區間合并問題

區間合并&#xff1a;區間合并問題 區間合并 www.acwing.com/problem/content/805/ 按區間的左端點排序 掃描整個區間&#xff0c;在這過程中把可能有交點的區間合并 全包含&#xff1a;不做改動相交&#xff1a;right 后移相離&#xff1a;更新至下一個維護區間 import j…

中國古代數學符號的演進 | 算籌 / 符號 / 算法

注&#xff1a;本文為“中國古代數學符號”相關合輯。 圖片清晰度受引文原圖所限。 略作重排&#xff0c;未整理去重。 如有內容異常&#xff0c;請看原文。 這個中國古代的數學瑰寶&#xff0c;到底厲害在哪&#xff1f; 原創 朱一文 科普中國 2024 年 07 月 31 日 15:30 北…

XMLDecoder、LDAP 注入與修復

問題&#xff1a;XMLDecoder注入 針對 xml 解碼器的注入攻擊 反序列化用戶控制的 XML &#xff0c;程序沒有進行驗證&#xff0c; 會讓攻擊者有機會在服務器上執行惡意代 碼。 例&#xff1a;下面代碼片段中&#xff0c; XMLDecoder 處理不可信的輸入。 ... XMLDecode…

Unity 對象層級處理小結

一.第一優先級Camera Culling Mask屬性指定Camera顯示的Layer,可以多選 Depth:Depth大的Camera顯示的Layer顯示在前面 二.避免使用PositionZ調整遮擋關系 在 2D 游戲中,雖然可以通過 Z 軸來調整顯示順序,但這與 2D 游戲的設計理念不符。在可以控制顯示層級的多個要素或方…

python基礎舉例

最近又重新開始學python&#xff0c;淺淺記錄下學習到的東西&#xff08;也方便自己回顧看&#xff09; 縮進、空格對于python很重要&#xff0c;一定要注意&#xff01; 以下代碼是基于pycharm編寫的。 01 輸出 #注釋 # 單行注釋用# &#xff0c;ctrl/是單行注釋的快捷鍵 # …

開疆智能ModbusTCP轉Canopen網關連接匯川PLC配置案例

本案例是通過開疆智能研發的ModbusTCP轉Canopen網關將匯川PLC與陀螺儀連接進行組網通訊。 準備階段 軟件&#xff1a;InoProShop(V1.7.3)&#xff0c;CANopen Configuration Studio PLC&#xff1a;匯川AC801-0221-R0R0 網關&#xff1a;開疆智能ModbusTCP轉Canopen網關 陀…

Tess4J:基于 Java 的 OCR 解決方案

在現代軟件開發中&#xff0c;圖像識別與文本提取已成為許多應用場景中的關鍵環節。OCR&#xff08;Optical Character Recognition&#xff09; 技術使得從圖像中提取文字成為可能。Tess4J 是一個基于 Java 的 OCR 開發庫&#xff0c;它封裝了 Google Tesseract OCR 引擎的本地…

Vue3 + JavaScript 父組件點擊按鈕觸發子組件事件方法

在 Vue 3 中&#xff0c;父組件點擊按鈕觸發子組件事件有以下三種常用方式&#xff1a; 方法 1&#xff1a;使用 ref 直接調用子組件方法&#xff08;推薦&#xff09; vue 復制 下載 <!-- 父組件 --> <template><button click"callChildMethod"…

超強人工智能解決方案套件InfiniSynapse:精準的業務理解、對各種數據源進行全模態聯合智能分析--部署安裝@Ubuntu22.04 @Docker

InfiniSynapse 通過自研的第二代LLM-Native RAG實現了企業業務的理解&#xff0c;精準的Schema召回保證數據的準確性。提供專門為大模型優化的InfiniSQL語言&#xff0c;從而可以更加準確的生成查詢語句&#xff0c;通過 InfiniSQL 引擎讓人類第一次對存儲在各種數據源的全模態…

解決國內無法加載谷歌驗證碼(reCAPTCHA):URL 重定向配置指南

解決國內無法加載谷歌驗證碼&#xff08;reCAPTCHA&#xff09;&#xff1a;URL 重定向配置指南 在搭建網站或使用某些應用時&#xff0c;經常會遇到需要調用谷歌驗證&#xff08;reCAPTCHA&#xff09;API 的情況。然而&#xff0c;由于網絡環境的特殊性&#xff0c;國內多數…

【Qt】如何使用QtInstallerFramework打包Qt程序

使用 Qt Installer Framework 可以將你的 Qt 程序打包成一個帶有安裝向導的安裝包&#xff0c;適用于 Windows、Linux 和 macOS 平臺。以下是完整的打包流程&#xff0c;以你當前開發的 ecgexport 應用為例。 &#x1f9f0; 一、準備工作 1. 安裝 Qt Installer Framework 下載…

如何編寫高效的Prompt:從入門到精通

在人工智能時代&#xff0c;特別是隨著大型語言模型(LLM)如ChatGPT、Claude等的普及&#xff0c;編寫高質量的Prompt(提示詞)已成為一項關鍵技能。一個好的Prompt可以顯著提高AI輸出的質量和相關性&#xff0c;而一個糟糕的Prompt可能導致無用甚至誤導性的結果。本文將帶你深入…

智慧工地云平臺源碼,基于微服務架構+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平臺系統&#xff0c;智慧工地全套源碼&#xff0c;java版智慧工地源碼&#xff0c;支持PC端、大屏端、移動端。 智慧工地聚焦建筑行業的市場需求&#xff0c;提供“平臺網絡終端”的整體解決方案&#xff0c;提供勞務管理、視頻管理、智能監測、綠色施工、安全管…

【機械視覺】Halcon—【十三、實例找各個區域面積和中心點】

找區域面積和中心點 *獲取圖像 read_image (Image, fabrik) *關閉窗口 dev_close_window () *打開窗口 dev_open_window (0, 0, 512, 512, black, WindowID) *設置輸出字體&#xff0c;14號字&#xff0c;Courier字體&#xff0c;粗體 set_display_font (WindowID, 14, mono, …