【算法】手撕快速排序

快速排序的思想

任取一個元素作為樞軸,然后想辦法把這個區間劃分為兩部分,小于等于樞軸的放左邊,大于等于樞軸的放右邊

然后遞歸處理左右區間,直到空或只剩一個

具體動畫演示詳見?

數據結構合集 - 快速排序(算法過程, 效率分析, 穩定性分析)

?Lomuto 分區方案(單邊掃描法)

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);
}private static void subSort(int[] nums, int low, int high){if(low < high){int pos = partition(nums, low, high);subSort(nums, low, pos-1);subSort(nums, pos+1, high);}
}private static int partition(int[] nums, int low, int high){int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}// 將pivot放到正確的位置int temp = nums[i + 1];nums[i + 1] = nums[high];nums[high] = temp;return i+1;
}

進一步優化上述代碼

優化點:小數組時改用插入排序

當待排序數組較小的時候,快速排序的遞歸開銷會比插入排序更大

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);}private static void subSort(int[] nums, int low, int high) {if (low < high) {// 小數組改用插入排序(閾值可調整,通常 10~20)if (high - low < 10) {insertionSort(nums, low, high);return;}int pos = partition(nums, low, high);subSort(nums, low, pos - 1);subSort(nums, pos + 1, high);}}private static void insertionSort(int[] nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}private static int partition(int[] nums, int low, int high){int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}// 將pivot放到正確的位置int temp = nums[i + 1];nums[i + 1] = nums[high];nums[high] = temp;return i+1;}

優化點:三數取中法選擇pivot避免最壞情況

如果pivot選擇最左或最右,在已排序或接近排序的數組上會導致最壞的情況O(n^{2}

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);}private static void subSort(int[] nums, int low, int high) {if (low < high) {// 小數組改用插入排序(閾值可調整,通常 10~20)if (high - low < 10) {insertionSort(nums, low, high);return;}int pos = partition(nums, low, high);subSort(nums, low, pos - 1);subSort(nums, pos + 1, high);}}private static void insertionSort(int[] nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}private static int partition(int[] nums, int low, int high){// 三數取中法選擇 pivotint mid = low + (high - low) / 2;if (nums[low] > nums[mid]) swap(nums, low, mid);if (nums[low] > nums[high]) swap(nums, low, high);if (nums[mid] > nums[high]) swap(nums, mid, high);// 將中位數放到 nums[high]swap(nums, mid, high);int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;swap(nums, i, j);}}// 將pivot放到正確的位置swap(nums, i + 1, high);return i+1;}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

優化點:尾遞歸優化避免棧溢出

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);}private static void subSort(int[] nums, int low, int high) {// 小數組改用插入排序if (high - low < 10) {insertionSort(nums, low, high);return;}// 尾遞歸優化while (low < high) {int pos = partition(nums, low, high);if (pos - low < high - pos) {subSort(nums, low, pos - 1);low = pos + 1;} else {subSort(nums, pos + 1, high);high = pos - 1;}}}private static void insertionSort(int[] nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}private static int partition(int[] nums, int low, int high){// 三數取中法選擇 pivotint mid = low + (high - low) / 2;if (nums[low] > nums[mid]) swap(nums, low, mid);if (nums[low] > nums[high]) swap(nums, low, high);if (nums[mid] > nums[high]) swap(nums, mid, high);// 將中位數放到 nums[high]swap(nums, mid, high);int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;swap(nums, i, j);}}// 將pivot放到正確的位置swap(nums, i + 1, high);return i+1;}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

優化點:雙軸快排

java的Arrays.sort對小數組用插入排序,對大數組用雙軸快排(比單軸更快)

雙軸快排的基本思路:

  1. 選取兩個 pivot
    • pivot1 = nums[left](較小的 pivot)
    • pivot2 = nums[right](較大的 pivot)
    • 確保 pivot1 ≤ pivot2(否則交換)
  1. 分區
    • [left, i)< pivot1
    • [i, k)≥ pivot1 && ≤ pivot2
    • [k, j]:未處理區間
    • (j, right]> pivot2
  1. 遞歸處理三個子數組
    • [left, i-1](小于 pivot1 的部分)
    • [i, j](介于 pivot1pivot2 之間的部分)
    • [j+1, right](大于 pivot2 的部分)
public static void quickSort(int[] nums){dualPivotQuickSort(nums, 0, nums.length-1);}private static void dualPivotQuickSort(int[] nums, int left, int right) {if (left >= right) return;// 確保 pivot1 ≤ pivot2if (nums[left] > nums[right]) {swap(nums, left, right);}int pivot1 = nums[left];int pivot2 = nums[right];int i = left + 1;  // [left+1, i) 存儲 < pivot1 的元素int k = left + 1;  // [i, k) 存儲 ≥ pivot1 && ≤ pivot2 的元素int j = right - 1; // (j, right-1] 存儲 > pivot2 的元素while (k <= j) {if (nums[k] < pivot1) {swap(nums, i, k);i++;k++;} else if (nums[k] <= pivot2) {k++;} else {swap(nums, k, j);j--;}}// 將 pivot1 和 pivot2 放到正確位置swap(nums, left, i - 1);swap(nums, right, j + 1);// 遞歸處理三個子數組dualPivotQuickSort(nums, left, i - 2);   // < pivot1dualPivotQuickSort(nums, i, j);          // pivot1 ≤ x ≤ pivot2dualPivotQuickSort(nums, j + 2, right);  // > pivot2}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

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

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

相關文章

《八大排序算法》

相關概念 排序&#xff1a;使一串記錄&#xff0c;按照其中某個或某些關鍵字的大小&#xff0c;遞增或遞減的排列起來。穩定性&#xff1a;它描述了在排序過程中&#xff0c;相等元素的相對順序是否保持不變。假設在待排序的序列中&#xff0c;有兩個元素a和b&#xff0c;它們…

深度學習篇---paddleocr正則化提取

文章目錄 前言一、代碼總述&介紹1.1導入必要的庫1.1.1cv21.1.2re1.1.3paddleocr 1.2初始化PaddleOCR1.3打開攝像頭1.4使用 PaddleOCR 進行識別1.5定義正則表達式模式1.6打印提取結果1.7異常處理 二、正則表達式2.1簡介2.2常用正則表達式模式及原理2.2.1. 快遞單號模式2.2.2…

JavaScript DOM與元素操作

目錄 DOM 樹、DOM 對象、元素操作 一、DOM 樹與 DOM 對象 二、獲取 DOM 元素 1. 基礎方法 2. 現代方法&#xff08;ES6&#xff09; 三、修改元素內容 四、修改元素常見屬性 1. 標準屬性 2. 通用方法 五、通過 style 修改樣式 六、通過類名修改樣式 1. className 屬…

單元測試的編寫

Python 單元測試示例 在 Python 中&#xff0c;通常使用 unittest 模塊來編寫單元測試。以下是一個簡單的示例&#xff1a; 示例代碼&#xff1a;calculator.py # calculator.py def add(a, b):return a bdef subtract(a, b):return a - b 單元測試代碼&#xff1a;test_c…

大模型學習:從零到一實現一個BERT微調

目錄 一、準備階段 1.導入模塊 2.指定使用的是GPU還是CPU 3.加載數據集 二、對數據添加詞元和分詞 1.根據BERT的預訓練&#xff0c;我們要將一個句子的句頭添加[CLS]句尾添加[SEP] 2.激活BERT詞元分析器 3.填充句子為固定長度 代碼解釋&#xff1a; 三、數據處理 1.…

10組時尚復古美學自然冷色調肖像電影照片調色Lightroom預設 De La Mer – Nautical Lightroom Presets

De La Mer 預設系列包含 10 種真實的調色預設&#xff0c;適用于肖像、時尚和美術。為您的肖像攝影帶來電影美學和個性&#xff01; De La Mer 預設非常適合專業人士和業余愛好者&#xff0c;可在桌面或移動設備上使用&#xff0c;為您的攝影項目提供輕松的工作流程。這套包括…

SDL多窗口多線程渲染技術解析

SDL多窗口多線程渲染技術解析 技術原理 SDL多線程模型與窗口管理 SDL通過SDL_Thread結構體實現跨平臺線程管理。在多窗口場景中,每個窗口需關聯獨立的渲染器,且建議遵循以下原則: 窗口與渲染器綁定:每個窗口創建時生成專屬渲染器(SDL_CreateRenderer),避免跨線程操作…

QT 跨平臺發布指南

一、Windows 平臺發布 1. 使用 windeployqt 工具 windeployqt --release --no-compiler-runtime your_app.exe 2. 需要包含的文件 應用程序 .exe 文件 Qt5Core.dll, Qt5Gui.dll, Qt5Widgets.dll 等 Qt 庫 platforms/qwindows.dll 插件 styles/qwindowsvistastyle.dll (如果使…

L2-037 包裝機 (分數25)(詳解)

題目鏈接——L2-037 包裝機 問題分析 這個題目就是模擬了物品在傳送帶和筐之間的傳送過程。傳送帶用隊列模擬&#xff0c;筐用棧模擬。 輸入 3 4 4 GPLT PATA OMSA 3 2 3 0 1 2 0 2 2 0 -1輸出 根據上述操作&#xff0c;輸出的物品順序是&#xff1a; MATA樣例分析 初始…

機器學習的一百個概念(4)下采樣

前言 本文隸屬于專欄《機器學習的一百個概念》&#xff0c;該專欄為筆者原創&#xff0c;引用請注明來源&#xff0c;不足和錯誤之處請在評論區幫忙指出&#xff0c;謝謝&#xff01; 本專欄目錄結構和參考文獻請見[《機器學習的一百個概念》 ima 知識庫 知識庫廣場搜索&…

qt6下配置qopengl

qt部件選擇 Qt 6&#xff1a;需要手動選擇 Qt Shader Tools 和 Qt 5 Compatibility Module&#xff08;如果需要兼容舊代碼&#xff09; cmake文件 cmake_minimum_required(VERSION 3.16) # Qt6 推薦最低 CMake 3.16 project(myself VERSION 0.1 LANGUAGES CXX)set(CMAKE_A…

數據安全系列4:密碼技術的應用-接口調用的身份識別

傳送門 數據安全系列1&#xff1a;開篇 數據安全系列2&#xff1a;單向散列函數概念 數據安全系列3&#xff1a;密碼技術概述 什么是認證&#xff1f; 一談到認證&#xff0c;多數人的反應可能就是"用戶認證" 。就是應用系統如何識別用戶的身份&#xff0c;直接…

STL之map和set

1. 關聯式容器 vector、list、deque、 forward_list(C11)等&#xff0c;這些容器統稱為序列式容器&#xff0c;因為其底層為線性序列的數據結構&#xff0c;里面存儲的是元素本身。 關聯式容器也是用來存儲數據的&#xff0c;與序列式容器不同的是&#xff0c;其里面存儲的是結…

Vue3 其它API Teleport 傳送門

Vue3 其它API Teleport 傳送門 在定義一個模態框時&#xff0c;父組件的filter屬性會影響子組件的position屬性&#xff0c;導致模態框定位錯誤使用Teleport解決這個問題把模態框代碼傳送到body標簽下

C++練習

1.將File練習題&#xff0c;內部的FILE*描述符&#xff0c;改成int描述符 2。寫一個類Fifo管道類。提高難度&#xff0c;什么都不提示。只要求&#xff1a;使用自己編寫的Fifo類對象&#xff0c;實現2個終端之間互相聊天 file.cpp #include <iostream> #include <c…

《Python Web網站部署應知應會》No4:基于Flask的調用AI大模型的高性能博客網站的設計思路和實戰(上)

基于Flask的調用AI大模型的高性能博客網站的設計思路和實戰&#xff08;上&#xff09; 摘要 本文詳細探討了一個基于Flask框架的高性能博客系統的設計與實現&#xff0c;該系統集成了本地AI大模型生成內容的功能。我們重點關注如何在高并發、高負載狀態下保持系統的高性能和…

實現一個簡易版的前端監控 SDK

【簡易版的前端監控系統】 1、Promise的錯誤如何監控&#xff1f;–promise不是所有都是接口請求 2、接口的報錯如何監控&#xff1f;–全局監控sdk&#xff0c;不改動公共的請求方法、不改動業務代碼&#xff1b;一般接口使用axios請求 3、資源的報錯如何監控&#xff1f; 4、…

【操作系統】軟中斷vs硬中斷

在操作系統中&#xff0c;中斷&#xff08;Interrupt&#xff09; 是 CPU 響應外部事件的重要機制&#xff0c;分為 硬中斷&#xff08;Hardware Interrupt&#xff09; 和 軟中斷&#xff08;Software Interrupt&#xff09;。它們的核心區別在于 觸發方式 和 處理機制。 1. 硬…

力扣刷題-熱題100題-第27題(c++、python)

21. 合并兩個有序鏈表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常規法 創建一個新鏈表&#xff0c;遍歷list1與list2&#xff0c;將新鏈表指向list1與list2…

Python包下載路徑 Chrome用戶數據 修改到非C盤

查看 site-packages 是否能通過命令行完成&#xff1f; 可以&#xff0c;使用以下命令&#xff08;不需寫腳本&#xff09;&#xff1a; python -m site輸出包含&#xff1a; sys.path site-packages 路徑&#xff08;全局和用戶級&#xff09; 如果只想看安裝路徑&#…