排序【各種題型+對應LeetCode習題練習】

目錄

常用排序

快速排序

LeetCode 912 排序數組

歸并排序

LeetCode 912 排序數組


常用排序

名稱排序方式時間復雜度是否穩定
快速排序分治O(n log n)
歸并排序分治O(n log n)
冒泡排序交換O(n2)
插入排序插入O(n2)
選擇排序選擇最值O(n2)
C++ STL sort快排+內省排序O(n log n)
C++ STL stable_sort歸并排序O(n log n)

快速排序

快速排序的核心思想是分治法,所以我們先來了解什么是分治

分治(Divide and Conquer) 是一種常見的算法設計思想,核心是三步:
Divide(劃分)
把一個大問題劃分成若干個小問題,通常是遞歸進行的。
Conquer(解決)
把每個子問題單獨解決,直到子問題足夠小,可以直接解決。
Combine(合并)
將子問題的解合并,得到原問題的解。

快速排序就是一個經典的分治應用:

步驟具體操作
Divide(劃分)從數組中選一個 基準值pivot,把數組劃分成兩部分:
左邊:所有小于等于 pivot 的元素
右邊:所有大于 pivot 的元素
Conquer(遞歸排序)對左子數組和右子數組分別遞歸快速排序
Combine(組合)排序完成后,把左右子數組 + pivot 合成一個完整的有序數組

快速排序 C++ 手寫模板(雙指針+原地交換)

void quickSort(vector<int>& nums, int left, int right) {if (left >= right) return;  // 遞歸終止條件int pivot = nums[left];     // 選擇最左側元素作為基準int i = left + 1, j = right;while (i <= j) {// 找到左邊大于 pivot 的位置while (i <= j && nums[i] <= pivot) i++;// 找到右邊小于 pivot 的位置while (i <= j && nums[j] > pivot) j--;if (i < j) swap(nums[i], nums[j]);  // 交換兩個指針位置的值}swap(nums[left], nums[j]);  // 將 pivot 放到正確位置quickSort(nums, left, j - 1);  // 排左邊quickSort(nums, j + 1, right); // 排右邊
}

調用方式:

vector<int> nums = {4, 2, 7, 1, 5};
quickSort(nums, 0, nums.size() - 1);

LeetCode 912 排序數組

思路:
1. 選擇一個基準值 pivot(通常選最左邊的值)
2. 雙指針 i / j 從兩邊往中間找:
i 找第一個大于 pivot 的數
j 找第一個小于等于 pivot 的數
如果 i < j,就交換
3.?最后把 pivot 放到分界點位置 → 即 j 位置
4.?遞歸地快排左邊、右邊子數組
?

class Solution {
public:void quickSort(vector<int>& nums, int left, int right) {if (left >= right) return;int pivot = nums[left];     // 選最左邊為 pivotint i = left + 1, j = right;while (i <= j) {while (i <= j && nums[i] <= pivot) i++; while (i <= j && nums[j] > pivot) j--;  if (i < j) swap(nums[i], nums[j]);      }swap(nums[left], nums[j]);  quickSort(nums, left, j - 1);  quickSort(nums, j + 1, right); }vector<int> sortArray(vector<int>& nums) {quickSort(nums, 0, nums.size() - 1);return nums;}
};

上面的代碼的基準值 pivot 選擇了最左邊的值,如果提交到LeetCode,會有部分案例超出時間限制,比如nums =?[3, 2, 2, 2, ..., 2]? ?這里的2有上千個,快速排序在該極端情況下退化成 O(n2)?
因為數組中大量元素與 pivot 相等,導致“遞歸深度很深”、“每次只處理一點點”,最終就退化成了冒泡排序,時間復雜度變為 O(n2)。

解決方案是改成隨機選 pivot,防止極端退化
這種隨機選擇基準值位置的方法在全是 2 的子數組中能夠高概率選中中間位置的 2 作為基準值,劃分后左右子數組長度 ≈ n/2,時間復雜度平均為?O(n log n)

class Solution {
public:void quickSort(vector<int>& nums, int left, int right) {if (left >= right) return;// 隨機選一個 pivot,避免最壞情況int randomIndex = rand() % (right - left + 1) + left;swap(nums[left], nums[randomIndex]);int pivot = nums[left];int i = left + 1, j = right;while (i <= j) {while (i <= j && nums[i] <= pivot) i++;while (i <= j && nums[j] > pivot) j--;if (i < j) swap(nums[i], nums[j]);}swap(nums[left], nums[j]);quickSort(nums, left, j - 1);quickSort(nums, j + 1, right);}vector<int> sortArray(vector<int>& nums) {srand(time(0)); quickSort(nums, 0, nums.size() - 1);return nums;}
};

但是上面的時間復雜度是平均為 O(n log n),可以使用三路快排,它針對大量重復值時更高效,即小于 pivot 的放左邊,等于 pivot 的放中間,大于 pivot 的放右邊
這種方法時間復雜度始終為 O(n log n)

class Solution {
public:void quickSort3Way(vector<int>& nums, int left, int right) {if (left >= right) return;// 隨機選 pivot,放到最左邊int randomIndex = rand() % (right - left + 1) + left;swap(nums[left], nums[randomIndex]);int pivot = nums[left];int lt = left;       // 小于 pivot 的最后位置int i = left + 1;    // 當前掃描的位置int gt = right;      // 大于 pivot 的起始位置while (i <= gt) {if (nums[i] < pivot) {swap(nums[i], nums[lt + 1]);lt++;i++;} else if (nums[i] > pivot) {swap(nums[i], nums[gt]);gt--;} else {  // nums[i] == pivoti++;}}swap(nums[left], nums[lt]);// 遞歸左右兩邊quickSort3Way(nums, left, lt - 1);quickSort3Way(nums, gt + 1, right);}vector<int> sortArray(vector<int>& nums) {srand(time(0));quickSort3Way(nums, 0, nums.size() - 1);return nums;}
};

上面代碼將數組劃分為三部分:
[left, lt-1](小于pivot)、[lt, gt](等于pivot)、[gt+1, right](大于pivot)

假設輸入為 [3, 2, 2, 2, 2, 4, 1]
pivot = 2
lt 區(<2):[1]
= 區(=2):[2,2,2,2]
gt 區(>2):[3,4]
遞歸只需要再排 [1] 和 [3,4],大量相等的值不需要再遞歸,消除了重復元素對性能的影響。

下面來模擬三路快排的劃分過程
數組為 [3, 2, 2, 2, 2, 4, 1]? 假設 pivot = 2(隨機選中了值為 2 的元素,放到了最左邊位置)

快排前準備狀態:

變量說明
pivot2基準值,隨機選后放最左邊
lt0< pivot 區右邊界
i1當前正在看的元素位置
gt6> pivot 區左邊界
nums[2, 3, 2, 2, 2, 4, 1]初始數組,pivot = 2 放在最左邊

三路快排過程跟蹤表

步驟i 指向值操作numsltgti
13> pivot → 與 gt=6 交換[2, 1, 2, 2, 2, 4, 3]051
21< pivot → 與 lt+1 交換[2, 1, 2, 2, 2, 4, 3] (1 與自己)152
32= pivot → i++[2, 1, 2, 2, 2, 4, 3]153
42= pivot → i++[2, 1, 2, 2, 2, 4, 3]154
52= pivot → i++[2, 1, 2, 2, 2, 4, 3]155
64> pivot → 與 gt=5 交換[2, 1, 2, 2, 2, 4, 3](不變)145

最后i=5 > gt=4? 跳出循環
循環結束后處理 pivot
swap(nums[left], nums[lt]) → 把 pivot 放回等于區前端
交換前:[2, 1, 2, 2, 2, 4, 3]
交換后:[1, 2, 2, 2, 2, 4, 3]

最終分區情況

區域元素含義
< pivot 區[1]nums[0]
= pivot 區[2, 2, 2, 2]nums[1~4]
> pivot 區[4, 3]nums[5~6]

然后遞歸:
左邊:quickSort3Way(nums, 0, 0) → 單個元素,無需處理
右邊:quickSort3Way(nums, 5, 6) → 處理 [4, 3]

歸并排序

歸并排序是穩定排序的經典算法,時間復雜度穩定為 O(n log n),適用于大規模數據的排序。
歸并排序采用的是分治法

歸并排序流程
假設要排序的數組為 [38, 27, 43, 3, 9, 82, 10]

分解階段:
將數組分成兩半: [38, 27, 43] 和 [3, 9, 82, 10]
繼續遞歸分解,直到每個子數組只剩一個元素。

合并階段:
合并 [38] 和 [27],得到 [27, 38]
合并 [43] 和 [27, 38],得到 [27, 38, 43]
對右半部分繼續類似的操作。

最終合并:
將兩個有序的子數組 [27, 38, 43] 和 [3, 9, 10, 82] 合并成一個有序的數組 [3, 9, 10, 27, 38, 43, 82]

LeetCode 912 排序數組

思路:
1. 使用 歸并排序 的方法:
遞歸地將數組分解為兩部分。
合并時保持數組的順序。
2. 合并時,比較左右兩部分的元素,確保數組有序。

class Solution {
public:
void merge(vector<int>& nums, int left, int mid, int right) {int n1 = mid - left + 1;  // 左子數組的大小int n2 = right - mid;     // 右子數組的大小vector<int> leftArr(n1), rightArr(n2);  // 創建兩個臨時數組// 復制數據到臨時數組for (int i = 0; i < n1; i++) leftArr[i] = nums[left + i];for (int i = 0; i < n2; i++) rightArr[i] = nums[mid + 1 + i];// 合并兩個臨時數組到原數組numsint i = 0;      // 左子數組索引int j = 0;      // 右子數組索引int k = left;   // 原數組起始索引while (i < n1 && j < n2) {  // 如果左子數組和右子數組還有元素if (leftArr[i] <= rightArr[j]) {nums[k] = leftArr[i];  // 左邊小的放到原數組i++;} else {nums[k] = rightArr[j]; // 右邊小的放到原數組j++;}k++;}// 復制剩余的元素while (i < n1) {   nums[k] = leftArr[i];i++;k++;}while (j < n2) {  nums[k] = rightArr[j];j++;k++;}
}void mergeSort(vector<int>& nums, int left, int right) {if (left >= right) return;int mid = left + (right - left) / 2;// 遞歸分割數組mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);// 合并兩個有序子數組merge(nums, left, mid, right);}vector<int> sortArray(vector<int>& nums) {mergeSort(nums, 0, nums.size() - 1);return nums;}
};

nums = [5, 2, 3, 1]?

初始數組:[5,2,3,1]
調用 mergeSort(0,3)
├─ 計算 mid=1
├─ 調用 mergeSort(0,1) ?// 處理 [5,2]
│ ?├─ 計算 mid=0
│ ?├─ 調用 mergeSort(0,0) → 終止([5])
│ ?├─ 調用 mergeSort(1,1) → 終止([2])
│ ?└─ 合并 → [2,5]
├─ 調用 mergeSort(2,3) ?// 處理 [3,1]
│ ?├─ 計算 mid=2
│ ?├─ 調用 mergeSort(2,2) → 終止([3])
│ ?├─ 調用 mergeSort(3,3) → 終止([1])
│ ?└─ 合并 → [1,3]
└─ 合并 [2,5] 和 [1,3] → [1,2,3,5]


尚未完結

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

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

相關文章

鴻蒙與web混合開發雙向通信

鴻蒙與web混合開發雙向通信用runJavaScript和registerJavaScriptProxy web entry/src/main/resources/rawfile/1.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content&q…

unity Physics.RaycastNonAlloc

Physics.RaycastNonAlloc 是 Unity 中用于 3D 物理射線檢測的高性能方法&#xff0c;它是 Physics.Raycast 的非分配版本。 方法簽名 public static int RaycastNonAlloc(Ray ray, RaycastHit[] results, float maxDistance Mathf.Infinity, int layerMask DefaultRaycastLay…

數據庫(five day finally)——物物而不物于物,念念而不念于念。(數據庫到此結束!祝世間美好與各位不期而遇,善意常伴汝身!)

1.子查詢&#xff08;1&#xff09;where 子查詢①多行單列配合in和not in操作&#xff08;類似于數據范圍查詢&#xff09;例&#xff1a;顯示工資與各個經理相同的雇員信息&#xff08;包含經理本身&#xff09;。select * from empwhere sal(select sal from emp where jobM…

【甲烷數據集】Sentinel-5P 衛星獲取的全球甲烷數據集-TROPOMI L2 CH?

目錄 數據概述 傳感器 & 衛星信息 監測目標:甲烷(CH?) 數據產品內容 空間與時間覆蓋 云篩選與協同觀測 技術文檔資源 數據下載 Python 代碼繪制 CH4 數據 參考 數據概述 Sentinel-5 Precursor Level 2 Methane (TROPOMI L2 CH?) 數據集是由歐洲哥白尼計劃的 Sentinel…

【數據結構】單鏈表練習(有環)

1.判斷是否是環形鏈表 141. 環形鏈表 - 力扣&#xff08;LeetCode&#xff09; bool hasCycle(struct ListNode *head) {struct ListNode *fast,*slow;fastslowhead;while(fast&&fast->next){fastfast->next->next;slowslow->next;if(fastslow)return tr…

VR 污水廠初體驗:顛覆傳統認知?

第一次戴上 VR 設備走進 VR 污水廠時&#xff0c;那種震撼的感覺至今難以忘懷。仿佛一瞬間&#xff0c;我被傳送到了一個全新的世界&#xff0c;平日里只能在圖紙或實地看到的污水廠&#xff0c;此刻就立體地呈現在眼前。腳下是縱橫交錯的管道&#xff0c;頭頂巨大的處理設備有…

父類 div 自適應高度 子類如何撐滿其高度

使用絕對定位 如果你想要子元素完全撐滿父元素的高度&#xff0c;可以使用絕對定位。這種方法適用于當子元素需要完全覆蓋父元素時。<div class"parent"><div class"child"><!-- 子類內容 --></div> </div>.parent {positio…

從0開始學習R語言--Day51--PH檢驗

在用cox回歸做分析時&#xff0c;我們一般會得出各種變量在結局的風險影響&#xff08;HR大于1&#xff0c;就代表變量值增大&#xff0c;對應結局影響的風險就隨之增大&#xff09;&#xff0c;但是這里有個壞處是&#xff0c;cox回歸得到的是瞬時風險值&#xff0c;我們最多得…

Docker 網絡原理

Linux 常見網絡虛擬化 虛擬網卡:tun/tap虛擬網卡&#xff08;又稱虛擬網絡適配器&#xff09;&#xff0c;即用軟件模擬網絡環境&#xff0c;模擬網絡適配器。在計算機網絡中&#xff0c;tun 與 tap 是操作系統內核中的虛擬網絡設備。不同于普通靠硬件網絡適配器實現的設備&…

【通識】PCB文件

1. PCB文件的導入 在PORTEL99 PCB編輯器的文件菜單中選擇導入先前繪制的CAD文件。導入成功后&#xff0c;編輯器將顯示出元件封裝的基本圖形&#xff0c;為后續操作奠定基礎。將需要抄板的PCB放置于掃描儀中隨后啟動掃描儀&#xff0c;之后啟動AUTO CAD軟件&#xff0c;之后插入…

分布式彈性故障處理框架——Polly(1)

1 前言之服務雪崩 在我們實施微服務之后&#xff0c;服務間的調用變得異常頻繁&#xff0c;多個服務之前可能存在互相依賴的關系&#xff0c;當某個服務出現故障或者是因為服務間的網絡出現故障&#xff0c;導致服務調用的失敗&#xff0c;進而影響到某個業務服務處理失敗&…

【機器學習深度學習】大模型推理速度與私有化部署的價值分析

目錄 前言 一、主流推理框架速度對比 二、為什么 HuggingFace 框架更適合微調驗證&#xff1f; 三、大模型私有化部署的必要性分析 ? 私有化部署的主要動因 1. 數據隱私與業務安全 2. 可控性與性能保障 ? 哪些情況不建議私有部署&#xff1f; 四、總結與選型建議 &…

elementui-admin構建

1、vue-element-admin vue-element-admin是基于element-ui 的一套后臺管理系統集成方案。 功能&#xff1a;介紹 | vue-element-adminA magical vue adminhttps://panjiachen.github.io/vue-element-admin-site/zh/guide/# GitHub地址&#xff1a;https://github.com/PanJia…

深入排查:編譯環境(JDK)與運行環境(JRE/JDK)不一致時的常見 Java 錯誤及解決方案

深入排查&#xff1a;編譯環境&#xff08;JDK&#xff09;與運行環境&#xff08;JRE/JDK&#xff09;不一致時的常見 Java 錯誤及解決方案 在后端 Java 項目中&#xff0c;編譯環境&#xff08;JDK&#xff09; 與 運行環境&#xff08;JRE/JDK&#xff09; 版本不一致&…

[JS逆向] 微信小程序逆向工程實戰

博客配套代碼與工具發布于github&#xff1a;微信小程序 &#xff08;歡迎順手Star一下?&#xff09; 相關爬蟲專欄&#xff1a;JS逆向爬蟲實戰 爬蟲知識點合集 爬蟲實戰案例 逆向知識點合集 前言&#xff1a; 微信小程序對于很多嘗試JS逆向的人群來說&#xff0c;都是一個…

基于5G系統的打孔LDPC編碼和均勻量化NMS譯碼算法matlab性能仿真

目錄 1.引言 2.算法仿真效果演示 3.數據集格式或算法參數簡介 4.算法涉及理論知識概要 4.1打孔技術 4.2 均勻量化NMS譯碼 5.參考文獻 6.完整算法代碼文件獲得 1.引言 在5G通信系統中&#xff0c;信道編碼技術是保障高速率、高可靠性數據傳輸的核心支撐&#xff0c;而低…

基于Java標準庫讀取CSV實現天地圖POI分類快速導入PostGIS數據庫實戰

目錄 前言 一、天地圖POI分類簡介 1、數據表格 2、分類結構 二、從CSV導入到PG數據庫 1、CSV解析流程 2、數據轉換及入庫 3、入庫成果及檢索 三、總結 前言 在之前的博客中&#xff0c;曾經對高德地圖和百度地圖的POI分類以及使用PostGIS數據庫來進行管理的模式進行了詳…

人-AI交互中的信息論不同于傳統的信息論,其信息的增量≠不確定性的減量

在人機交互&#xff08;Human-AI Interaction, HAI&#xff09;領域&#xff0c;信息論的應用確實與傳統的信息論有所不同。這種差異主要源于人機交互HAI中信息的復雜性、動態性以及人類認知的特點。1. 傳統信息論的核心概念傳統信息論由克勞德香農&#xff08;Claude Shannon&…

K8s 通過 Scheduler Extender 實現自定義調度邏輯

1. 為什么需要自定義調度邏輯 什么是所謂的調度? 所謂調度就是指給 Pod 對象的 spec.nodeName 賦值 待調度對象則是所有 spec.nodeName 為空的 Pod 調度過程則是從集群現有的 Node 中為當前 Pod 選擇一個最合適的 實際上 Pod 上還有一個平時比較少關注的屬性&#xff1a;…

7.19 換根dp | vpp |滑窗

lcr147.最小棧通過兩個棧 維護實現class MinStack { public:stack<int> A, B;MinStack() {}void push(int x) {A.push(x);if(B.empty() || B.top() > x)B.push(x);}void pop() {if(A.top() B.top())B.pop();A.pop();}int top() {return A.top();}int getMin() {retur…