【數據結構】——排序篇(中)

前面我們已經了解了幾大排序了,那么我們今天就來再了解一下剩下的快速排序法,這是一種非常經典的方法,時間復雜度是N*logN。

在這里插入圖片描述

快速排序法:

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

我們的快速排序可以通過遞歸和非遞歸來實現,我們先來看看遞歸實現快排,我們的遞歸快排又分為三個版本,三種方法各有各的特點,我們接下來就來看看吧。

需要調用的函數代碼:

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;// begin midi end 三個數選中位數if (a[begin] < a[midi]){if (a[midi] < a[end])return midi;else if (a[begin] > a[end])return begin;elsereturn end;}else // a[begin] > a[midi]{if (a[midi] > a[end])return midi;else if (a[begin] < a[end])return begin;elsereturn end;}
}

hoare版本單趟排序:

// hoare
int PartSort1(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){// 右邊找小while (left < right && a[right] >= a[keyi]){--right;}// 左邊找大while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);return left;
}

這個版本的單趟排序理解起來很容易,但是我們的坑點比較多,所以難度對于我們而言還是有點挑戰性,我們需要兩個變量來實現,我們的left從左邊數組的首元素開始走,找比keyi位置大的元素,keyi代表的就是我們數組首元素的下標,我們用right從最后一個元素開始走,找比keyi位置小的元素,找到了一個大的元素和一個小的元素就交換,但是我們的數組中可能有多個相等的元素,所以我們內嵌循環就得用<=。我們最后left和right相遇時結束,相遇位置的右邊的元素大于等于keyi位置的元素,而相遇位置左邊的元素都小于等于keyi位置的元素,我們最后就將left位置的元素和keyi位置的元素交換再返回left就完成了。

挖坑法單趟排序:

int PartSort2(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int key = a[begin];int hole = begin;while (begin < end){// 右邊找小,填到左邊的坑while (begin < end && a[end] >= key){--end;}a[hole] = a[end];hole = end;// 左邊找大,填到右邊的坑while (begin < end && a[begin] <= key){++begin;}a[hole] = a[begin];hole = begin;}a[hole] = key;return hole;
}

我們先將第一個數據存放在臨時變量key中形成一個坑位,我們同樣用到left和right,同樣的用法,left從第一個元素往后走,right從最后一個元素往前走。我們的right先走,找到一個比hole下標小的元素就于hole下標的元素交換,交換之后left再走,找到一個比hole位置大的元素就與hole位置的元素交換,然后再left位置形成一個坑點。然后又是right走,交換后left走,反復進行,最后在相遇的時候就結束循環。因為我們的hole下標的值都是空的,所以在最后我們將key的數據給hole下標的坑位就可以了,最后再返回坑位的數據。具體的過程如下圖所示:
在這里插入圖片描述

前后指針版本單趟排序:

int PartSort3(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int keyi = begin;int prev = begin;int cur = prev + 1;while (cur <= end){if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}

我們的前后指針版本看起來比前兩個版本更加的容易,因為我們的兩個指針prev指向我們數組首元素的下標,而cur是我們首元素的下一個元素的下標,我們的cur先走,如果我們指向的元素比keyi位置的元素大的話我們就cur++向前走一位,如果比keyi位置的元素小的話我們就prev++向前走一位再與cur位置的交換,cur再繼續往前走知道cur>end的時候就結束循環。最后我們再將prev位置的數據給keyi位置就完成了。
在這里插入圖片描述

單趟優化版本:

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if (end - begin + 1 <= 10){InsertSort(a + begin, end - begin + 1);}else{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){// 右邊找小while (left < right && a[right] >= a[keyi]){--right;}// 左邊找大while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;// [begin, keyi-1] keyi [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}
}

單趟優化版本就是我們的區間數據不大時,我們用直接插入排序法,而數據太大的時候我們就用hoare版本的單趟排序。

函數遞歸實現快排:

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int keyi = PartSort2(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

實現我們只需要調用一個版本的單趟排序在進行遞歸就可以了。我們代碼中是用的挖坑法的單趟排序,我們就直接調用PartSort2就可以了。

遞歸的我們已經了解完了,那么我們就來看看非遞歸的怎么實現吧:

void QuickSortNonR(int* a, int begin, int end)
{ST s;STInit(&s);STPush(&s, end);STPush(&s, begin);while (!STEmpty(&s)){int left = STTop(&s);STPop(&s);int right = STTop(&s);STPop(&s);int keyi = PartSort3(a, left, right);// [left, keyi-1] keyi [keyi+1, right]if (left < keyi - 1){STPush(&s, keyi - 1);STPush(&s, left);}if (keyi + 1 < right){STPush(&s, right);STPush(&s, keyi + 1);}}STDestroy(&s);
}

非遞歸版本的快速排序。我們需要起始位置 begin 和結束位置 end。首先,函數創建一個棧 st,用于存儲待處理的子數組的起始和結束位置。將 end 和 begin 分別壓入棧中,表示對整個數組進行排序。進入循環,只要棧不為空,從棧中彈出兩個元素,分別賦值給 left 和 right,表示當前要處理的子數組的起始和結束位置。調用 PartSort3 函數對子數組進行分區,得到基準元素的位置 keyi。根據分區的結果,將子數組劃分為 [left, keyi-1]、[keyi]、[keyi+1, right] 三個部分。如果 keyi + 1 < right,說明右側子數組仍然有元素需要排序,將右側子數組的起始位置 keyi + 1 和結束位置 right 壓入棧中。如果 left < keyi - 1,說明左側子數組仍然有元素需要排序,將左側子數組的起始位置 left 和結束位置 keyi - 1 壓入棧中。循環繼續進行,直到棧為空,表示所有子數組都被處理完畢。最后,銷毀棧 st,就完成了非遞歸版本的快速排序。
在這里插入圖片描述

如果對大家有所幫助的話就支持一下吧!

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

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

相關文章

C++ queue 和priority_queue

目錄 1.什么是queue 2.模擬實現 3.仿函數 模板參數Compare 仿函數 4.什么是priority_queue 模擬實現 1.什么是queue 1.隊列是一種容器適配器&#xff0c;專門用于在FIFO上下文(先進先出)中操作&#xff0c;其中從容器一端插入元素&#xff0c;另一端提取元素。 2.隊列作為…

Java轉Go學習之旅 | Go入門(1)

入門 命令行參數找出重復行常規版本涉及文件操作 命令行參數 命令行參數以os包中Args名字的變量供程序訪問&#xff0c;在os包外面&#xff0c;使用os.Args這個名字 變量os.Args是一個字符串sliceos.Args[0]&#xff1a;命令本身的名字os.Args[1:]&#xff1a;另外的元素&…

Cglib動態代理從入門到掌握

Cglib 動態代理 本文的寫作目的是為了探究 Spring 框架中在使用Transactional標注的方法中使用 this 進行自調用時事務失效的原因&#xff0c;各種視頻教程中只是簡單指出 this 指向的不是代理類對象&#xff0c;而是目標類對象&#xff0c;但是并沒有解釋為什么 this 不是代理…

麒麟系統使用桌面共享遠程桌面

客戶端安裝vinager 服務端 安裝 vnc4server xrdp tightvncserver vino 安裝完成后 需要重啟 在用戶的家目錄下新建 .xsession 寫入xfce4-session防止閃退 雪花屏 開啟xrdp服務 遠程鏈接 Vnc只能鏈接系統登錄的用戶 Rdp可以鏈接所有普通用戶

vscode插件webview和插件通信

如果你要在 VS Code 插件的 WebView 中調用插件中的方法&#xff0c;可以使用 vscode.postMessage API。具體步驟如下&#xff1a; 在插件中&#xff0c;在創建 WebView 時&#xff0c;指定一個 onDidReceiveMessage 回調方法&#xff0c;該方法會在 WebView 中調用 vscode.po…

【C語言】結構體內存對齊

目錄 引入結構體 結構的聲明 創建和初始化 內部元素的使用&#xff1b; 特殊聲明&#xff1a; 結構體在內存中的對齊 練習&#xff1a; 引入結構體 C語言有各種數據類型&#xff0c;我們已經對一些數據類型很熟悉&#xff1a; 整型&#xff08;int&#xff09;- 存儲整…

力扣-151. 反轉字符串中的單詞

文章目錄 看下去&#xff0c;你一定可以理解此題&#xff0c;寫的簡單易懂力扣題目解題思路函數構成1.反轉函數2.消除掉多余空格函數 整體函數 看下去&#xff0c;你一定可以理解此題&#xff0c;寫的簡單易懂 力扣題目 給你一個字符串 s &#xff0c;請你反轉字符串中 單詞 …

京東商品詳情數據在數據分析行業中的重要性

京東商品詳情數據在數據分析行業中具有重要作用。這些數據提供了豐富的信息&#xff0c;可以幫助企業了解市場趨勢、消費者需求、產品表現以及運營策略等多個方面。 首先&#xff0c;京東商品詳情數據可以為企業提供市場趨勢分析的依據。通過觀察商品的銷售量、銷售額、價格等…

c語言:理解和避免野指針

野指針的定義&#xff1a; 野指針是指一個指針變量存儲了一個無效的地址&#xff0c;通常是一個未初始化的指針或者指向已經被釋放的內存地址。當程序嘗試使用野指針時&#xff0c;可能會導致程序崩潰、內存泄漏或者其他不可預測的行為。因此&#xff0c;在編程中需要特別注意…

Pandas中DataFrame對象的創建與常用屬性方法(第2講)

Pandas中DataFrame對象的創建與常用屬性方法(第2講) ??????? ??博主 侯小啾 感謝您的支持與信賴。?? ???????????????????????????????????????????????????????????????????????????…

智能優化算法應用:基于孔雀算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼

智能優化算法應用&#xff1a;基于孔雀算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼 文章目錄 智能優化算法應用&#xff1a;基于孔雀算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼1.無線傳感網絡節點模型2.覆蓋數學模型及分析3.孔雀算法4.實驗參數設定5.算法結果6.參考文獻7.MATLAB…

[足式機器人]Part2 Dr. CAN學習筆記-數學基礎Ch0-2 特征值與特征向量

本文僅供學習使用 本文參考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN學習筆記-數學基礎Ch0-2 特征值與特征向量 1. 定義1.1 線性變換1.2 求解特征值&#xff0c;特征向量1.3 應用&#xff1a;對角化矩陣——解耦Decouple 2. Summary 1. 定義 A v ? λ v ? A\vec{v}\lambd…

【網絡奇緣】- 計算機網絡|深入學習物理層|網絡安全

? &#x1f308;個人主頁: Aileen_0v0&#x1f525;系列專欄: 一見傾心,再見傾城 --- 計算機網絡~&#x1f4ab;個人格言:"沒有羅馬,那就自己創造羅馬~" 回顧鏈接&#xff1a;http://t.csdnimg.cn/ZvPOS 這篇文章是關于深入學習原理參考模型-物理層的相關知識點&…

Linux權限命令詳解

Linux權限命令詳解 文章目錄 Linux權限命令詳解一、什么是權限&#xff1f;二、權限的本質三、Linux中的用戶四、linux中文件的權限4.1 文件訪問者的分類&#xff08;人&#xff09;4.2 文件類型和訪問權限&#xff08;事物屬性&#xff09; 五、快速掌握修改權限的做法【第一種…

Spark-Streaming+Kafka+mysql實戰示例

文章目錄 前言一、簡介1. Spark-Streaming簡介2. Kafka簡介二、實戰演練1. MySQL數據庫部分2. 導入依賴3. 編寫實體類代碼4. 編寫kafka主題管理代碼5. 編寫kafka生產者代碼6. 編寫Spark-Streaming代碼總結前言 本文將介紹一個使用Spark Streaming和Kafka進行實時數據處理的示例…

實戰1-python爬取安全客新聞

一般步驟&#xff1a;確定網站--搭建關系--發送請求--接受響應--篩選數據--保存本地 1.拿到網站首先要查看我們要爬取的目錄是否被允許 一般網站都會議/robots.txt目錄&#xff0c;告訴你哪些地址可爬&#xff0c;哪些不可爬&#xff0c;以安全客為例子 2. 首先測試在不登錄的…

Docker Network(網絡)——8

目錄&#xff1a; Docker 為什么需要網絡管理Docker 網絡架構簡介 CNMLibnetwork驅動常見網絡類型 bridge 網絡host 網絡container 網絡none 網絡overlay 網絡docker 網絡管理命令 docker network createdocker network inspectdocker network connectdocker network disconne…

class072 最長遞增子序列問題與擴展【算法】

class072 最長遞增子序列問題與擴展【算法】 code1 300. 最長遞增子序列 // 最長遞增子序列和最長不下降子序列 // 給定一個整數數組nums // 找到其中最長嚴格遞增子序列長度、最長不下降子序列長度 // 測試鏈接 : https://leetcode.cn/problems/longest-increasing-subsequen…

830. 單調棧

?????? ??????830. 單調棧 - AcWing題庫 給定一個長度為 N 的整數數列&#xff0c;輸出每個數左邊第一個比它小的數&#xff0c;如果不存在則輸出 ?1?1。 輸入格式 第一行包含整數 N&#xff0c;表示數列長度。 第二行包含 N個整數&#xff0c;表示整數數列…

你知道MySQL中 group by 怎么優化嗎

更好的閱讀體驗&#xff0c;請點擊 YinKai s Blog。 ? 在 MySQL 中 group by 用于按照一個或多個列對結果集進行分組。在討論 group by 怎么優化之前&#xff0c;我們先來看看 group by 的執行流程&#xff0c;這樣我們才能對癥下藥。 group by 執行流程 ? 我們先用下面的 …