【算法基礎】快速排序算法 - JAVA

一、算法基礎

1.1 什么是快速排序

快速排序(Quick Sort)是一種高效的分治排序算法,由英國計算機科學家Tony Hoare于1960年提出。它的核心思想是:

  • 選擇一個基準元素(pivot)
  • 將數組分成兩部分:小于基準的元素和大于基準的元素
  • 遞歸地對這兩部分進行排序

快速排序是實際應用中最常用的排序算法之一,平均情況下時間復雜度為O(n log n),空間復雜度為O(log n)

1.2 快速排序的基本思想

  1. 選擇基準:從數組中選擇一個元素作為基準(通常是第一個元素、最后一個元素或中間元素)
  2. 分區操作:將數組中小于基準的元素放在基準的左邊,大于基準的元素放在右邊
  3. 遞歸排序:對基準左右兩部分分別進行遞歸排序

這個過程被稱為分區(Partition),是快速排序的核心操作。

1.3 時間復雜度分析

  • 最佳情況:O(n log n) —— 每次分區操作都將數組均勻地分成兩部分
  • 平均情況:O(n log n) —— 大多數情況下的性能表現
  • 最差情況:O(n2) —— 當數組已經有序或幾乎有序時,選擇第一個或最后一個元素作為基準

二、快速排序的實現

2.1 基本實現

public class QuickSort {public static void sort(int[] arr) {if (arr == null || arr.length <= 1) {return;}quickSort(arr, 0, arr.length - 1);}private static void quickSort(int[] arr, int left, int right) {if (left < right) {// 獲取分區點int pivotIndex = partition(arr, left, right);// 遞歸排序左半部分quickSort(arr, left, pivotIndex - 1);// 遞歸排序右半部分quickSort(arr, pivotIndex + 1, right);}}private static int partition(int[] arr, int left, int right) {// 選擇最右邊的元素作為基準int pivot = arr[right];// i是小于基準區域的邊界int i = left - 1;// 遍歷區間,將小于基準的元素放到左邊for (int j = left; j < right; j++) {if (arr[j] <= pivot) {i++;// 交換元素swap(arr, i, j);}}// 將基準元素放到正確的位置swap(arr, i + 1, right);// 返回基準元素的索引return i + 1;}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

2.2 遞歸過程分析

假設有數組:[8, 4, 2, 9, 5, 7, 6, 1, 3]

  1. 第一次分區

    • 選擇基準值 3(最后一個元素)
    • 分區后:[1, 2, 3, 9, 5, 7, 6, 8, 4]
    • 基準索引:2
  2. 左子數組遞歸:[1, 2]

    • 選擇基準值 2
    • 分區后:[1, 2]
    • 基準索引:1
  3. 右子數組遞歸:[9, 5, 7, 6, 8, 4]

    • 選擇基準值 4
    • 分區后:[4, 5, 7, 6, 8, 9]
    • 基準索引:0
  4. 繼續遞歸...

最終得到排序結果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

三、快速排序的優化

3.1 基準選擇優化

選擇好的基準可以顯著提高快速排序的性能。最常用的優化方法是三數取中(Median-of-Three):

private static int selectPivot(int[] arr, int left, int right) {int mid = left + (right - left) / 2;// 將三個元素排序if (arr[left] > arr[mid]) {swap(arr, left, mid);}if (arr[left] > arr[right]) {swap(arr, left, right);}if (arr[mid] > arr[right]) {swap(arr, mid, right);}// 將中間值(基準)交換到right-1位置swap(arr, mid, right - 1);return arr[right - 1];
}

3.2 小數組使用插入排序

對于小規模數組(通常小于10個元素),插入排序比快速排序更高效:

private static void quickSort(int[] arr, int left, int right) {// 小數組使用插入排序if (right - left <= 10) {insertionSort(arr, left, right);return;}// 正常的快速排序過程if (left < right) {int pivotIndex = partition(arr, left, right);quickSort(arr, left, pivotIndex - 1);quickSort(arr, pivotIndex + 1, right);}
}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;}
}

3.3 尾遞歸優化

通過將遞歸調用替換為迭代,可以避免棧溢出:

private static void quickSort(int[] arr, int left, int right) {while (left < right) {int pivotIndex = partition(arr, left, right);// 遞歸處理較小的子數組,迭代處理較大的子數組if (pivotIndex - left < right - pivotIndex) {quickSort(arr, left, pivotIndex - 1);left = pivotIndex + 1;} else {quickSort(arr, pivotIndex + 1, right);right = pivotIndex - 1;}}
}

四、典型應用:荷蘭國旗問題

荷蘭國旗問題是快速排序的一個典型應用,它要求將數組分成三個部分:小于、等于、大于某個值的元素。這種分區方法也稱為三向切分

4.1 問題描述

給定一個數組和一個基準值,將數組重新排列,使得所有小于基準的元素在左邊,等于基準的元素在中間,大于基準的元素在右邊。

4.2 解法實現

public static void dutchFlagPartition(int[] arr, int pivot) {int left = 0;       // 小于pivot的區域右邊界int current = 0;    // 當前處理的元素int right = arr.length - 1;  // 大于pivot的區域左邊界while (current <= right) {if (arr[current] < pivot) {// 小于pivot的元素放左邊swap(arr, left, current);left++;current++;} else if (arr[current] > pivot) {// 大于pivot的元素放右邊swap(arr, current, right);right--;// 注意:此時不增加current,因為交換來的元素還未處理} else {// 等于pivot的元素保持原位current++;}}
}

4.3 應用到快速排序

使用三向切分可以優化快速排序,特別是處理有大量重復元素的數組:

private static void quickSort3Way(int[] arr, int left, int right) {if (left >= right) {return;}// 記錄小于、等于、大于pivot的三個區域邊界int lt = left;      // 小于pivot的區域右邊界int gt = right;     // 大于pivot的區域左邊界int i = left + 1;   // 當前處理的元素int pivot = arr[left];while (i <= gt) {if (arr[i] < pivot) {swap(arr, lt++, i++);} else if (arr[i] > pivot) {swap(arr, i, gt--);} else {i++;}}// 遞歸處理小于和大于的部分quickSort3Way(arr, left, lt - 1);quickSort3Way(arr, gt + 1, right);
}

五、完整實現與示例

以下是一個包含各種優化的完整快速排序實現:

public class OptimizedQuickSort {private static final int INSERTION_SORT_THRESHOLD = 10;public static void sort(int[] arr) {if (arr == null || arr.length <= 1) {return;}quickSort(arr, 0, arr.length - 1);}private static void quickSort(int[] arr, int left, int right) {// 小數組使用插入排序if (right - left <= INSERTION_SORT_THRESHOLD) {insertionSort(arr, left, right);return;}// 使用三數取中法選擇基準medianOfThree(arr, left, right);int pivot = arr[right - 1];// 分區int i = left;int j = right - 1;for (;;) {while (arr[++i] < pivot) {}while (arr[--j] > pivot) {}if (i >= j) break;swap(arr, i, j);}// 將基準放回正確位置swap(arr, i, right - 1);// 遞歸排序quickSort(arr, left, i - 1);quickSort(arr, i + 1, right);}private static void medianOfThree(int[] arr, int left, int right) {int mid = left + (right - left) / 2;if (arr[left] > arr[mid]) {swap(arr, left, mid);}if (arr[left] > arr[right]) {swap(arr, left, right);}if (arr[mid] > arr[right]) {swap(arr, mid, right);}// 將基準(中間值)放到right-1位置swap(arr, mid, right - 1);}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;}}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public static void main(String[] args) {int[] arr = {8, 4, 2, 9, 5, 7, 6, 1, 3};System.out.println("原始數組: " + Arrays.toString(arr));sort(arr);System.out.println("排序后數組: " + Arrays.toString(arr));}
}

六、總結

核心要點

  1. 分治思想:快速排序采用分治策略,通過分區操作將問題分解為子問題
  2. 基準選擇:基準元素的選擇直接影響算法效率,好的選擇可以避免最壞情況
  3. 就地排序:快速排序是一種原地排序算法,不需要額外的數組空間
  4. 不穩定性:快速排序不是穩定排序算法,相同元素的相對順序可能改變

優缺點

優點:

  • 平均情況下,性能優于其他 O(n log n) 排序算法
  • 不需要額外的存儲空間(原地排序)
  • 對緩存友好,局部性好

缺點:

  • 最壞情況下,時間復雜度為 O(n2)
  • 不穩定排序,無法保持相等元素的相對順序
  • 對于小數組,可能不如插入排序高效

適用場景

  • 需要高效排序大數組時
  • 內存受限、不能使用額外空間時
  • 當平均性能比最壞情況性能更重要時
  • 不要求排序穩定性時

快速排序是一種高效且實用的排序算法,在大多數情況下都表現出色。通過本文介紹的各種優化技巧,可以使快速排序在實際應用中獲得最佳性能。掌握快速排序是每位程序員的基本功,也是理解分治算法思想的重要一步。

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

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

相關文章

Linux用戶管理命令和用戶組管理命令

一、用戶管理命令 1.1、adduser 添加新用戶 1、基本語法 adduser 用戶名 &#xff08;功能描述&#xff1a;添加新用戶&#xff09; 應用場景1&#xff1a;企業開發&#xff0c;多人協同&#xff08;也會有多人使用相同的一個低權限用戶&#xff09;。 應用場景2&#x…

記錄兩個免費開源又好用的后臺模版vue3

一.element-plus-admin 一套基于vue3、element-plus、typesScript、vite的后臺集成方案 1.簡介 vue-element-plus-admin 是一個基于 element-plus 免費開源的中后臺模版。使用了最新的 Vue3&#xff0c;Vite&#xff0c;Typescript等主流技術開發&#xff0c;開箱即用的中后…

Flip PDF Plus Corp7.7.22電子書制作軟件

flip pdf plus corporate7.7.22中文版由FlipBuilder官方出品的一款企業級的翻頁電子書制作軟件&#xff0c;擁有豐富的模板&#xff0c;主題和動畫場景&#xff0c;每本書最大頁數1000頁&#xff0c;每本書的最大大小1GB&#xff0c;即可以幫助企業用戶制作好豐富的電子書籍。 …

C語言藍橋杯真題代碼

以下是不同屆藍橋杯C語言真題代碼示例&#xff0c;供參考&#xff1a; 第十三屆藍橋杯省賽 C語言大學B組 真題&#xff1a;卡片 題目&#xff1a;小藍有很多數字卡片&#xff0c;每張卡片上都是數字1-9。他想拼出1到n的數列&#xff0c;每張卡片只能用一次&#xff0c;求最大的…

[Windows] Kazumi番劇采集v1.6.9:支持自定義規則+在線觀看+彈幕,跨平臺下載

[Windows] Kazumi番劇采集 鏈接&#xff1a;https://pan.xunlei.com/s/VOPLMhEQD7qixvAnoy73NUK9A1?pwdtu6i# Kazumi是一款基于框架; 開發的輕量級番劇采集工具&#xff0c;專為ACG愛好者設計。通過;自定義XPath規則; 實現精準內容抓取&#xff0c;支持多平臺&#xff08;An…

探秘數據結構:構建高效算法的靈魂密碼

摘要 數據結構作為計算機科學的基石&#xff0c;其設計與優化直接影響算法效率、資源利用和系統可靠性。本文系統闡述數據結構的基礎理論、分類及其核心操作&#xff0c;涵蓋數組、鏈表、棧、隊列、樹、圖、哈希表與堆等經典類型。深入探討各結構的應用場景與性能對比&#xf…

機器人--架構及設備

機器人的四大組成部分 控制系統 驅控系統 執行系統 電機屬于執行系統的設備。 傳感系統 傳感系統分為內部傳感系統和外部傳感系統。 內部傳感系統(內部傳感器)&#xff1a; 用于獲取機器人內部信息&#xff0c;比如IMU&#xff0c;力傳感器等。 外部傳感系統(外部傳感器):…

人工智能:如何快速篩選出excel中某列存在跳號的單元格位置?

前提&#xff1a; 電腦上必須提前安裝好了【office AI】軟件工具 方法如下&#xff1a; 1、打開要操作的excel表格&#xff0c;點擊上方的【officeAI】&#xff0c;再點擊左邊的【右側面板】按鈕&#xff0c;就會出現如下右側的【OfficeAI助手】 2、在OfficeAI助手的聊天框…

Spring MVC入門

介紹了Spring MVC框架的概念、特征及核心功能&#xff0c;通過案例詳細介紹了Spring MVC開發所需要的開發環境以及基本的開發步驟。 一、Spring MVC框架概述 Spring MVC是Spring框架的一個模塊&#xff0c;是一個基于Java的實現了MVC設計模式的輕量級Web框架。它通過一套注解和…

貪心算法求解邊界最大數

貪心算法求解邊界最大數&#xff08;拼多多2504、排列問題&#xff09; 多多有兩個僅由正整數構成的數列 s1 和 s2&#xff0c;多多可以對 s1 進行任意次操作&#xff0c;每次操作可以置換 s1 中任意兩個數字的位置。多多想讓數列 s1 構成的數字盡可能大&#xff0c;但是不能比…

Ubuntu ZLMediakit的標準配置文件(rtsp->rtmp->hls)

最近在工作中遇到不生成hls資源的問題,后面發現是配置文件有誤,特此記錄正確的config.ini配置文件,方便查閱。 最終解決方案,通過下面這種格式可以訪問到flv視頻,具體為什么不太清楚,rtmp格式:rtmp://39.113.48.113:8089/live/1744168516937396175 記錄最終解決方案:ht…

# LeetCode 1007 行相等的最少多米諾旋轉

LeetCode 1007 行相等的最少多米諾旋轉 原題英文&#xff1a;Minimum Domino Rotations For Equal Row 難度&#xff1a;中等 | 標簽&#xff1a;數組、貪心 1?題目重述 給定兩行長度相同的多米諾骨牌&#xff1a; tops[i] 表示第?i?張骨牌上面的數字&#xff1b;bottoms[…

大數據技術:從趨勢到變革的全景探索

??個人主頁??:一ge科研小菜雞-CSDN博客 ????期待您的關注 ???? 在數字化時代的浪潮下,大數據已經不再是一個陌生的概念。從日常生活中的社交媒體,到企業決策支持系統,再到公共管理的大數據應用,它正在改變著我們的工作和生活方式。隨著技術的進步,傳統的數據…

前端八股Day5——XHS某中廠實習前端一面

沒寫完&#xff0c;睡醒補 CSS盒模型 //出現頻率好高&#xff0c;感覺每次寫面經都遇到 W3C標準盒模型(content-box)&#xff1a;盒子寬高width/heightpaddingbordermargin IE怪異盒模型(border-box)&#xff1a;盒子寬高width/heigth(包括padding和border)margin 默認標準切換…

INP指標

什么是INP&#xff08;Interaction to Next Paint&#xff09; 參考網站&#xff1a;webVital-INP文檔 定義與核心目標 INP 是一項穩定的 Core Web Vitals 指標&#xff0c;通過統計用戶訪問期間所有符合條件的互動約定時間&#xff0c;評估網頁對用戶操作的總體響應能力。最…

剖析擴散模型(Denoising Diffusion Probabilistic Models)

文章目錄 1. 前言2. 前向擴散過程(Forward Diffusion)3. 反向生成過程&#xff08;Reverse Process&#xff09;4. 訓練和推理過程中的偽代碼5. 訓練過程代碼實現&#xff08;Training&#xff09;5.1 時間嵌入模塊——TimeEmbedding5.2 前向擴散過程——GaussianDiffusionTrai…

基于 Spring Boot 瑞吉外賣系統開發(九)

基于 Spring Boot 瑞吉外賣系統開發&#xff08;九&#xff09; 保存菜品 菜品管理頁面提供了一個“新增菜品”按鈕&#xff0c;單擊該按鈕時&#xff0c;會打開新增菜品頁面。 請求路徑/dish&#xff0c;請求方法POST&#xff0c;參數使用DishDto類接收。 DishDto 添加f…

w317汽車維修預約服務系統設計與實現

&#x1f64a;作者簡介&#xff1a;多年一線開發工作經驗&#xff0c;原創團隊&#xff0c;分享技術代碼幫助學生學習&#xff0c;獨立完成自己的網站項目。 代碼可以查看文章末尾??聯系方式獲取&#xff0c;記得注明來意哦~&#x1f339;贈送計算機畢業設計600個選題excel文…

【Agent搭建】利用coze平臺搭建一個AI銷售?

目錄 一、關于coze 核心功能 二、搭建屬于你自己智能體 備注&#xff1a;&#xff08;以下說明比較需要調整的板塊&#xff09; 1、從Prompt工程開始 2、搭建工作流 3、添加知識 三、總結 一、關于coze Coze是字節跳動推出的AI應用開發平臺&#xff0c;專注于幫助用戶快速…

Sharding-JDBC分庫分表中的熱點數據分布不均勻問題及解決方案

引言 在現代分布式應用中&#xff0c;使用Sharding-JDBC進行數據庫的分庫分表是提高系統性能和擴展性的常見策略。然而&#xff0c;在實際應用中&#xff0c;某些特定的數據&#xff08;如最新訂單、熱門商品等&#xff09;可能會成為“熱點”&#xff0c;導致這些部分的數據處…