快速排序遞歸和非遞歸方法的簡單介紹

基本思想為:任取待排序元素序列中 的某元素作為基準值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小于基準值,右 子序列中所有元素均大于基準值,然后最左右子序列重復該過程,直到所有元素都排列在相應位置上為止。

實現快速排序的單趟排序中有三種方法:1. 左右指針法;2. 挖坑法;3. 前后指針法

1. 左右指針法:其基本思想就是begin指針指向數組的首元素,begin作用是找大于key的值;end指針指向數組的末尾元素,end作用是找小于key的值;key為基準值此時選擇末尾元素作為基準值(選擇首元素作為基準),那么需要左邊先走也就是begin先移動(那么需要右邊先走也就是end移動)。begin找到大于key的值就和end位置的值交換(end找到小于begin的值就和begin位置的值交換),直到begin和end相遇,相遇后此位置就是key再數組最終有序后的位置。

畫圖舉例:如下圖所示key=8到了正確的位置,將數組分為左右兩部分,然后遞歸調用單趟(第一趟)排序的方法即可。注意:選右(左)邊為基準一定要讓左(右)邊先走,否則會出問題,具體畫圖得知

// 交換兩個數
void Swap(int* q1, int* q2)
{int temp = *q1;*q1 = *q2;*q2 = temp;
}// 單趟 左右指針法
int partionsort1(int* arr, int begin, int end)
{assert(arr);// 以下兩行代碼是三數取中間值解決快速排序最壞的情況的時間復雜度,后面講到了取消注釋即可//int midIndex = Getmidindex(arr, begin, end);//Swap(&arr[midIndex], &arr[end]);int keyindex = end; // 選右邊則begin左邊先走反之右邊先走while (begin < end){// begin先走 找大while (begin < end && arr[begin] <= arr[keyindex]) // 不加=號可能會導致死循環{++begin;}// end 找小while (begin < end && arr[end] >= arr[keyindex]){--end;}Swap(&arr[begin], &arr[end]);}Swap(&arr[begin], &arr[keyindex]);return begin;
}

2. 挖坑法:

選擇end位置為初始坑,用key保存end位置的值,begin移動找到大于key的值,將該值放到end處,此時begin處就是新的坑(數據可以被覆蓋);然后end移動找到小于key的值,將該值放到begin處,此時end處就是新的坑(數據可以被覆蓋)。重復下去直到begin和end相遇。key放到他們相遇后的位置即可

畫圖舉例:

// 方法2 挖坑法--這個方法比較好理解
int partionsort2(int* arr, int begin, int end)
{assert(arr);//int midIndex = Getmidindex(arr, begin, end);//Swap(&arr[midIndex], &arr[end]);// 第一個坑,初始坑,end位置就是坑,數據可以被覆蓋int key = arr[end];while (begin < end){while (begin < end && arr[begin] <= key){begin++;}// 左邊的值比key大,將begin的值填到end的坑上,begin位置就形成了新的坑arr[end] = arr[begin];while (begin < end && arr[end] >= key){end--;}// 右邊的值比key小,將end的值填到begin的坑上,end位置就形成了新的坑arr[begin] = arr[end];}// begin和end相遇后就是 這個位置就是坑,也就是key的最終位置arr[begin] = key;return begin;
}

3. 前后指針法:

選擇最后一個位置作為基準值key,prev指針指向首元素的前一個位置,cur指針指向首元素,當cur位置的數據小于key時,并且prev+1不等cur時將prev和cur位置的值互換,然后cur++;不進行互換時cur也++。當cur走到end位置后,只需要將key的值和prev+1位置的值互換即可。

畫圖舉例:

// 方法3 前后指針法
int partionsort3(int* arr, int begin, int end)
{assert(arr);//int midIndex = Getmidindex(arr, begin, end);//Swap(&arr[midIndex], &arr[end]);int keyindex = end;int prev = begin - 1;int cur = begin;while (cur < end){if (arr[cur] < arr[keyindex] && ++prev != cur)Swap(&arr[cur], &arr[prev]);cur++;}// 畫圖演示,最后是要將key和prev的下一個位置的數據進行交換,key的左邊才小于key右邊才大于keySwap(&arr[++prev], &arr[keyindex]);return prev;
}

遞歸方法:

利用函數遞歸調用以上三種方法中的其中一種即可:第一調用時,數組被分為了小于key的前半部分和大于key的后半部分,分別在調用函數對這兩部分進行排序即可

void QuickSort1(int* arr, int left, int right)
{assert(arr);if (left >= right)return;int div = partionsort1(arr, left, right);// 排左邊部分 [left,div-1] div [div+1 right]QuickSort1(arr, left, div - 1);// 排右邊部分QuickSort1(arr, div + 1, right);//if (left < right)//{//	int div = partionsort(arr, left, right);//	// 排左邊部分//	QuickSort(arr, left, div - 1);//	// 排右邊部分//	QuickSort(arr, div + 1, right);//}
}

快速排序的缺點:?數據已經有序的情況下每一次都選擇左邊或者右邊作為key,左邊或者右邊剩下很多數據,要繼續排序建棧幀,會導致棧溢出,比如 1w個有序數,就要建1W個棧幀,此時就是最壞的情況 。(時間復雜度為 O (n2))

前面提到三數取中間值的方法就能夠解決最壞的情況,基準值被選在數組中間位置,讓數組能較為均衡地被劃分,快速排序的時間復雜度得以維持在 O (nlogn)。

// 獲得前中后三個數中的中間值,解決快速排序的最壞的情況
int Getmidindex(int* arr, int begin, int end)
{assert(arr);int mid = (begin + end) / 2;if (arr[begin] < arr[mid]){if (arr[mid] < arr[end])return mid;else if (arr[begin] > arr[end])return begin;elsereturn end;}else  // arr[begin] > arr[mid]{if (arr[mid] > arr[end])return mid;else if (arr[end] > arr[begin])return begin;elsereturn end;}
}

優化:待排序的區間小于10的時候使用直接插入排序

// 優化
void QuickSort2(int* arr, int left, int right)
{assert(arr);if (left >= right)return;// 區間大于10if ((right - left + 1) > 10){int div = partionsort3(arr, left, right);// 排左邊部分 [left,div-1] div [div+1 right]QuickSort2(arr, left, div - 1);  // 左遞歸// 排右邊部分QuickSort2(arr, div + 1, right);  // 右遞歸}//區間小于10使用插入排序else{InsertSort(arr + left, right - left + 1);}//if (left < right)//{//	int div = partionsort(arr, left, right);//	// 排左邊部分//	QuickSort(arr, left, div - 1);//	// 排右邊部分//	QuickSort(arr, div + 1, right);//}
}

非遞歸方法:

遞歸的實現,就是給每一個遞歸函數建立函數棧幀,通過壓棧的方式存儲函數的參數。這里使用棧來模擬函數棧幀。

前面已經講到了棧的相關實現代碼,這里就直接使用了:

畫圖理解,下圖畫出了遞歸的區間變化圖,非遞歸只需要將left和right壓入棧中,再取出來即可傳給partionsort即可,只要棧不空,那么就有子區間還是無序狀態,棧空后數組就排序完成。

// 遞歸改非遞歸,1:改循環(斐波那契數列求解)一些簡單的遞歸才能改;2:棧模擬存儲數據
// 非遞歸作用:1:提高效率(建立函數棧幀還是有消耗的,但是對于現代計算機,這個優化微乎其微可以忽略不計)
//           2:遞歸最大缺陷就是,如果棧幀的深度太深,可能會導致棧溢出,因為系統的棧區空間一般不大在M級別,
//			 數據結構棧模擬非遞歸, 數據是存儲在堆區上面的,堆區空間是G級別的
// 快速排序--非遞歸方法--利用棧替代棧區(函數棧幀)
void QuickSortNonR(int* arr, int left, int right)
{assert(arr);Stack st;       // 建棧StackInit(&st); // 初始化棧// 將最開始的區間壓入棧StackPush(&st, right);StackPush(&st, left);while (!StackEmpty(&st)){// 數組左區間值int begin = StackTop(&st);  // 取棧頂數據StackPop(&st);              // 出棧// 數組右區間值int end = StackTop(&st);StackPop(&st);int div = partionsort2(arr, begin, end);  // 排序// [begin,div-1] div [div+1,end]// 如果剩下待排序的兩段區間的元素個數大于1個那么就入棧,再取出來排序// 和遞歸一樣先排左區間再排右區間if (div + 1 < end)  // 右區間,相當于右遞歸{StackPush(&st, end);StackPush(&st, div+1);}if (begin < div - 1)  // 左區間,相當于左遞歸{StackPush(&st, div - 1);StackPush(&st, begin);}}StackDetory(&st);
}

結論:

快速排序是一種基于分治思想的高效排序算法,其核心是通過基準值將數組劃分為左右兩部分,遞歸處理直至有序。本文詳細介紹了三種單趟排序實現方法:1)左右指針法通過雙指針交換元素;2)挖坑法通過移動基準值"坑位";3)前后指針法利用雙指針交替移動。針對遞歸可能導致的棧溢出問題,提出三數取中優化基準值選擇,并建議小規模數據使用插入排序。最后介紹了用棧模擬遞歸的非遞歸實現,有效避免深度遞歸風險。各種方法均附有代碼實現和圖示說明,完整展現了快速排序的優化思路。

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

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

相關文章

從零開始的云計算生活——第三十二天,四面楚歌,HAProxy負載均衡

目錄 一.HAProxy簡介 二.HAProxy特點和優點&#xff1a; 三.HAProxy保持會話的三種解決方法 四.HAProxy的balance 8種負載均衡算法 1&#xff09;RR&#xff08;Round Robin&#xff09; 2&#xff09;LC&#xff08;Least Connections&#xff09; 3&#xff09;SH&am…

策略模式及優化

策略模式&#xff08;Strategy Pattern&#xff09;是一種行為設計模式&#xff0c;其核心思想是將算法的定義與使用分離&#xff0c;使算法可以獨立于客戶端進行變化。它通過定義一系列算法&#xff0c;將每個算法封裝到獨立的類中&#xff0c;并使它們可以互相替換&#xff0…

微信小程序開發-桌面端和移動端UI表現不一致問題記錄

桌面端和移動端UI表現不一致零、引擎說明一、樣式不同1、text 單行&#xff1a;1.1 空格開發者工具不展示&#xff0c;手機/PC端正常1.2 正常展示省略號&#xff0c;需要2、點擊按鈕z-index: -1。webview - 桌面端不行&#xff0c; skyline - 移動端可以&#xff1b;3、其他說明…

極限狀態下函數開根號的計算理解(含示意圖)

遇到一個挺有意思的題做個記錄&#xff1a; 求曲線y (x21)(x2?1)0.5\frac{\left(x^{2}1\right)}{\left(x^{2}-1\right)^{0.5}}(x2?1)0.5(x21)?漸近線的條數 比較明顯的x 1是無定義點。但是在求極限的時候發現1和1-得到的極限值似乎不一樣。似乎是1是趨向于∞&#xff0c;1…

C++——模版(函數模版和類模版)

C 模板&#xff08;Templates&#xff09;完整介紹模板是 C 中一種強大的泛型編程機制&#xff0c;允許開發者編寫與類型無關的代碼&#xff0c;從而提高代碼的復用性和靈活性。通過模板&#xff0c;可以避免為不同數據類型重復編寫相似的函數或類&#xff0c;實現真正的代碼復…

Python之cv2:cv2(OpenCV,opencv-python)庫pip下載超時、下載失敗、無法下載的解決方案大全

Python之cv2&#xff1a;cv2(OpenCV&#xff0c;opencv-python)庫pip下載超時、下載失敗、無法下載的解決方案大全 在學習和使用 OpenCV&#xff08;Python 包名&#xff1a;opencv-python 或簡稱 cv2&#xff09;的過程中&#xff0c;很多初學者常常會遇到通過 pip install o…

asyncio 與 uvloop

事件循環 事件循環 協調所有協程執行的中央調度器&#xff0c;它通過非阻塞機制&#xff0c;實現并發執行多個異步任務。 事件循環是 異步編程的核心機制&#xff0c;用一句話概括就是&#xff1a; 事件循環不斷檢查任務隊列&#xff0c;一旦某個異步任務完成&#xff0c;它…

一文讀懂循環神經網絡(RNN)—語言模型+n元語法(1)

目錄 什么是語言模型&#xff1f; 語言模型的核心目的 一.量化文本的合理性 二.支持下游 NLP 任務 三. 語義和上下文依賴 一元語法、二元語法和三元語法詳解 核心概念&#xff1a;n-gram 模型 1. 一元語法&#xff08;Unigram&#xff09; 2. 二元語法&#xff08;Bigram…

DirectX12(D3D12)基礎教程九 間接繪制

在學習directx12 microsoft提供了很多示例&#xff0c;有簡單的也有復雜,下載網址&#xff1a;https://github.com/microsoft/DirectX-Graphics-Samples 本章對D3D12ExecuteIndirect 示例做了簡化&#xff0c;只保留間接繪制部分&#xff0c;刪除了計算著色器部分。 間接繪制…

fastApi連接數據庫

1&#xff1a;pip install tortoise-orm2&#xff1a;pip install aiomysql3&#xff1a;pip install asyncmy或者使用國內清華園pip install -i https://pypi.tuna.tsinghua.edu.cn/simple asyncmy4&#xff1a;pip install aerich通過 python -m 直接運行&#xff08;推薦&a…

Apache-web服務器環境搭建

目錄 實驗要求 思路總結 1.常規配置web服務 2.通過用戶主頁配置web服務 3.通過虛擬目錄配置web服務 4.添加DNS解析服務&#xff0c;訪問虛擬機域名&#xff1a; www.TestWeb.com 實驗要求 (ip 192.168.48.130) 1、常規配置web服務 2、通過用戶主頁配置web服務 3、通過虛…

Altium Designer 25 安裝與配置完整教程

本教程將帶您一步步完成 Altium Designer 25 的下載、安裝與激活配置 第一步&#xff1a;下載安裝包 首先&#xff0c;需要獲取 Altium Designer 25 的完整安裝程序。 &#x1f449; 下載鏈接&#xff1a; 百度網盤&#xff1a;百度網盤 請輸入提取碼 提取碼: dxei 夸克網盤…

【工具】AndroidStudio修改中文語言漢化

AndroidStudio修改中文語言漢化 https://github.com/sollyu/AndroidStudioChineseLanguagePackhttps://github.com/sollyu/AndroidStudioChineseLanguagePack

代碼隨想錄|圖論|15并查集理論基礎

并查集理論基礎 | 代碼隨想錄 并查集還是比較簡單的&#xff0c;只要搞清楚兩個事情&#xff1a; 并查集是干啥的&#xff1f;解決什么類型問題&#xff1f;并查集模板&#xff08;背下來&#xff09; 1、并查集是干啥的 并查集主要是兩個功能&#xff1a; 兩個元素添加到…

用MYSQL學習sql第一次總結和作業

總結 數據庫&#xff08;Database&#xff09; 理解為“文件夾”&#xff0c;里面可以裝很多張表。作業中要求先建一個名字叫 mydb6_product 的數據庫。 表&#xff08;Table&#xff09; 理解為“Excel 工作表”&#xff0c;由“列&#xff08;字段&#xff09;”和“行&…

SQLite技術架構解析,適用場景有哪些?

一、SQLite技術架構解析 SQLite是一款輕量級、無服務器、嵌入式關系型數據庫&#xff0c;其架構設計圍繞“簡化復雜性、提升效率”展開&#xff0c;核心由前端&#xff08;SQL處理&#xff09;、執行引擎&#xff08;VDBE&#xff09;、存儲引擎&#xff08;B-Tree&#xff09;…

【Luogu】每日一題——Day3. P6392 中意 (數學 取模)

鏈接&#xff1a;P6392 中意 - 洛谷 題目&#xff1a; 思路&#xff1a; 數論這一塊 題目讓我們求這個結果對 MOD 取模&#xff0c;那么我們肯定是不像看到這個除法&#xff0c;所以考慮如何消除這個除法 我們可以想到&#xff0c;向上取整就是加上一個數&#xff0c;假設其為…

React強大且靈活hooks庫——ahooks入門實踐之DOM類hook(dom)詳解

什么是 ahooks&#xff1f; ahooks 是一個 React Hooks 庫&#xff0c;提供了大量實用的自定義 hooks&#xff0c;幫助開發者更高效地構建 React 應用。其中 DOM 類 hooks 是 ahooks 的一個重要分類&#xff0c;專門用于處理 DOM 相關操作&#xff0c;如事件監聽、元素狀態、拖…

GeoTools 工廠設計模式

前言使用GeoTools開發時有必要了解其工廠設計模式&#xff0c;作為軟件開發核心設計模式&#xff0c;其設計思想具有普遍性和研究性。明白方法原理有助于提高開發效率&#xff0c;達到事半功倍的效果。1. 工廠模式 工廠模式&#xff08;Factory Pattern&#xff09;是面向對象中…

npu-smi info命令參數解釋

華為昇騰npu-smi顯示npu-smi工具的幫助信息npu-smi -h字段說明-h命令的幫助信息–help命令的幫助信息-vnpu-smi版本信息info顯示硬件詳細信息set修改設備配置屬性clear清除設備信息upgrade升級MCU固件 npu-smi info 用于監控和管理華為NPU的狀態和性能字段值說明npu-smi24.1.rc…