數據結構之快速排序算法(快排)【圖文詳解】

P. S.:以下代碼均在VS2019環境下測試,不代表所有編譯器均可通過。
P. S.:測試代碼均未展示頭文件stdio.h的聲明,使用時請自行添加。

??

在這里插入圖片描述

???????????????????????????????????????????博主主頁:LiUEEEEE
??????????????????? ??????????????????????????C語言專欄
????????????????????????????????????????????數據結構專欄
?????????????????????????????????????????力扣牛客經典題目專欄

目錄

  • 1、快排的基本思想
  • 2、快排的四種實現方法
  • 3、Hoare快速排序
  • 4、挖坑法快速排序
  • 5、前后指針法快速排序
  • 6、非遞歸法快速排序
  • 7、快速排序的優化
    • 7.1 三數取中
    • 7.2 少量數據另謀他法
  • 8、快速排序的特性總結
  • 9、結語

1、快排的基本思想


??快速排序是Hoare于1962年提出的一種二叉樹結構的交換排序方法,其基本思想為

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




2、快排的四種實現方法


??快速排序有多種實現方法,其具體體現如下:
  • Hoare快速排序
  • 挖坑法快速排序
  • 前后指針法快速排序
  • 非遞歸法快速排序

下文將對上述四種實現方法依次進行講解。




3、Hoare快速排序


??Hoare快速排序的示意圖如下所示:

在這里插入圖片描述
??其主要思想為:使用循環遍歷數組

  • 將所給數組的第一個值的下標給予key變量和begin變量(下文中統一為keyi,方便理解其為下標地址而非數組成員值),將數組最后一個值的下標給予end變量。
  • 開始執行是令end向數組左側遍歷,尋找數值比數組下標為keyi的值小的數,若未尋找到,則繼續向左側遍歷,若尋找到,則停止遍歷,轉而為begin向數組右側遍歷。
  • begin向數組右側遍歷,尋找數值比下標為keyi的值大的數,若尋找到,則交換數組中下標為end和begin的值。
  • 若在begin與end遍歷過程中兩變量出現相等的情況,則退出循環,將數組中下標為keyi和end的值交換。
  • 記錄下交換位置,令其為新的分割點,利用遞歸的思想,將數組根據新分割點分割的左右兩次子數組進行遞歸,直到出現新數組中L >= R。
  • 遞歸結束。


    ??其遞歸思想的圖示如下,示例:成員為4,2,6,5,7,1,3,8,10,9的數組。
    在這里插入圖片描述??當記錄交換位置為3,即會有新的子數組【0,2】【4,9】,使用新數組進行遞歸。

在這里插入圖片描述
??遞歸方法如上圖所示(除3外其余遞歸交換位置為虛構,需實際計算得到)。

  • Hoare快速排序代碼如下
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}int PartSort1(int* a, int left, int right)
{if (left >= right)return;int begin = left;int end = right;int keyi = left;while (begin < end){while(begin < end && a[end] >= a[keyi]){end--;}while (begin < end && a[begin] <= a[keyi]){begin++;}Swap(&a[end], &a[begin]);}Swap(&a[end], &a[keyi]);keyi = begin;PartSort1(a, left, keyi - 1);PartSort1(a, keyi + 1, right);
}




4、挖坑法快速排序


??挖坑法快速排序示意圖如下所示:

在這里插入圖片描述
??其主要思想為:使用循環遍歷數組

  • 將數組中keyi所在下標的值暫存起來,其位置看作坑位,先令end從右向左遍歷尋找比暫存值小的值,尋找到后,將end所指向的值放入坑中,而后end所指向的位置變成新坑。
  • 再令begin從右向左尋找比暫存值大的值,尋找到后,將begin所指向的值放入坑中,而后begin所指向位置變為新坑。
  • 在循環過程中若begin與end相遇,則將暫存之放入此時的坑中。
  • 記錄下最后一個坑的位置視作分界點,從分界點處分出兩個新的子數組,進行下一輪遞歸,直至所分出的新數組L >= R。
  • 遞歸結束。
    ??其遞歸示意圖與標題三中所展示一致,故不再做重復展示。

  • 挖坑法快速排序代碼如下所示:
int PartSort2(int* a, int left, int right)
{if (left >= right)return;int key = a[left];int keyi = left;int begin = left;int end = right;while (begin < end){while (begin < end && a[end] >= key){end--;}a[keyi] = a[end];keyi = end;while (begin < end && a[begin] <= key){begin++;}a[keyi] = a[begin];keyi = begin;}a[keyi] = key;PartSort2(a, left, end - 1);PartSort2(a, end + 1, right);
}




5、前后指針法快速排序


??前后指針法快速排序示意圖如下所示:

在這里插入圖片描述
??其主要思想為:

  • 定義指針prev指向數組開始位置,cur指向數組開始第二個位置,并使用keyi記錄數組中用作比較的數。

  • 令cur向數組右端遍歷,若出現小于keyi的數則停止遍歷,轉為prev進行遍歷。

  • 令prev向數組右端遍歷,若出現大于keyi的數,則令其與cur交換位置,而后繼續進行cur的遍歷,直至cur達到數組末尾的下一位(即越界)。

  • 當cur越界后將當前prev所代表的值與key所代表值進行交換,并將prev所處位置視為分界點,從分界點處分出兩個新的子數組進行遞歸,直至所遞歸的數組L >= R。

  • 遞歸結束。
    ??其遞歸示意圖與標題三中所展示一致,故不再做重復展示。

  • 前后指針法快速排序代碼如下所示:

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}int PartSort3(int* a, int left, int right)
{if (left >= right)return;int prev = left;int cur = prev + 1;int key = left;while (cur <= right){if (a[cur] < a[key] && a[++prev] != a[key])Swap(&a[cur], &a[prev]);cur++;}Swap(&a[key], &a[prev]);PartSort3(a, left, prev - 1);PartSort3(a, prev + 1, right);
}




6、非遞歸法快速排序


??非遞歸的方法實現快速排序,其原理是使用棧來模擬代碼在系統中的棧中的遞歸過程,也可稱之為偽遞歸。
??有關棧的相關內容可在下方查看:

????????????????????????????????棧(使用順序表構建)


??其主要思想為:
  • 使用棧來模擬系統中棧的功能,將數組的右左下標依次傳入棧中,每一次取其棧頂位置數據分別作為單詞快排的目標數組左下標和右下表(每一次取值后需刪除棧頂元素)。

  • 在單次快排完成后判斷其在分段點所分兩個新的子數組左右下標是否合理(是否存在左下標大于或等于右下標的行為),若不存在則將右子數組右左下標依次推入棧中,而后將左子數組右左下標依次推入棧中,進行下一輪循環。

  • 其循環判斷條件為棧中是否為空。

  • 有關其入棧順序可不做過多研究,但要保證的前提是所取出數值需與操作數組的左右下標相對應,以避免不必要麻煩。

  • 非遞歸法快速排序代碼如下所示:

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void QuickSortNonR(int* a, int left, int right)
{ST s;STInit(&s);STPush(&s, right);STPush(&s, left);while (!STEmpty(&s)){int begin = STTop(&s);STPop(&s);int end = STTop(&s);STPop(&s);int prev = begin;int cur = prev + 1;int key = begin;while (cur <= end){if (a[cur] < a[key] && a[++prev] != a[key])Swap(&a[cur], &a[prev]);cur++;}Swap(&a[key], &a[prev]);if (prev + 1 < end){STPush(&s, end);STPush(&s, prev + 1);}if (begin < prev - 1){STPush(&s, prev - 1);STPush(&s, begin);}}STDestroy(&s);
}




7、快速排序的優化


?? 此優化可適用于任意方法的可快速排序。
??在日常生活中,我們所使用排序功能的目標對象可能不會想日常練習中那樣少,或許是一個非常龐大的數量,且其所提供的數據可能在原本基礎上就有部分有序化,例如所給的大量數據中第一位為最小值或最大值,而這樣的數據我們從一開始就進行快速排序的話,會讓end從末尾一直遍歷到數據首部或begin從數據首部一直遍歷到末尾,擁有時間復雜度上的浪費,所以我們提出了一個 三數取中的方法來進行優化,且針對于大量數據,當所處理數據的子數據數量過少時,我們還是用快速排序的方法有點大材小用了,故針對于此我們也提出了 少量數據另謀他法的方法應對。

7.1 三數取中


??針對于所給目標數組第一位即為最小值或最大值而導致的時間復雜度浪費,我們可以取其數組首位,中位,末位三個數進行比較,選取其中處于中間位置的數,令其與數據首位進行交換。
  • 其代碼如下所示:
int GetMid(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] > a[mid]){if (a[mid] > a[right])return mid;else if (a[right] > a[left])return left;elsereturn right;}else{if (a[mid] < a[right])return mid;else if (a[right] < a[left])return left;elsereturn right;}
}

7.2 少量數據另謀他法


??對于根據分界點所得到的新子數組,若其數據量較小,我們還是用快速排序的化有些大材小用,故當數據量較小時,我們一般使用其他排序方法,如冒泡排序或插入排序等,進行快速的排序。
  • 其代碼如下所示
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){for (int j = 0; j < n - i - 1; j++){if (a[j] > a[j + 1])Swap(&a[j], &a[j + 1]);}}
}if (right - left < 10){BubbleSort(a, right - left + 1);}




??至此快速排序的優化結束,其完整代碼如下所示(前后指針法快速排序):

void QuickSort(int* a, int left, int right)
{if (left >= right)return;int mid = GetMid(a, left, right);Swap(&a[left], &a[mid]);if (right - left < 10){BubbleSort(a, right - left + 1);}else{int prev = left;int cur = prev + 1;int key = left;while (cur <= right){if (a[cur] < a[key] && a[++prev] != a[key])Swap(&a[cur], &a[prev]);cur++;}Swap(&a[key], &a[prev]);PartSort3(a, left, prev - 1);PartSort3(a, prev + 1, right);}
}




8、快速排序的特性總結


?? 快速排序的特性總結:
  1. 快速排序整體的綜合性能和使用場景都是比較好的,所以才敢叫快速排序
  2. 時間復雜度:O(N*logN)
  3. 空間復雜度:O(logN)
  4. 穩定性:不穩定




9、結語


在這里插入圖片描述

??十分感謝您觀看我的原創文章。
??本文主要用于個人學習和知識分享,學習路漫漫,如有錯誤,感謝指正。
??如需引用,注明地址。

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

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

相關文章

【Java數據結構】詳解Stack與Queue(三)

&#x1f512;文章目錄&#xff1a; 1.????前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2. 隊列&#xff08;Queue&#xff09; 2.1隊列的概念 2.2隊列的方法 2.3隊列的使用 2.4循環隊列 循環隊列的介紹 循環隊列圖 如何區分循環隊列是滿還是空…

外掛知識庫的基本知識與內容

外掛知識庫 1.什么是rag&#xff1f; RAG,即LLM在回答問題或生成文本時&#xff0c;會先從大量文檔中檢索出相關的信息&#xff0c;然后基于這些信息生成回答或文本&#xff0c;從而提高預測質量。 2.外掛知識庫的實現思路 只用幾十萬量級的數據對大模型進行微調并不能很好…

第五十六周:文獻閱讀

目錄 摘要 Abstract 文獻閱讀&#xff1a;應用于地表水總磷濃度預測的可解釋CEEMDAN-FE-LSTM-Transformer混合模型 一、現有問題 二、提出方法 三、方法論 1、CEEMDAN&#xff08;帶自適應噪聲的完全包絡經驗模式分解&#xff09; 2、FE&#xff08;模糊熵 &#xff09…

Vue3【十】07使用ref創建基本類型的響應式數據以及ref和reactive區別

Vue3【十】07使用ref創建基本類型的響應式數據以及ref和reactive區別 ref 也可以創建對象類型的響應式數據&#xff0c;不過要使用.value ref 處理對象數據的時候&#xff0c;底層數據還是reactive格式的 reactive 重新分配一個新對象&#xff0c;會失去響應式可以使用Object.a…

自注意力機學習

自注意力機制的核心概念 1. Query, Key 和 Value Query&#xff08;查詢向量&#xff09;&#xff1a;可以看作是你當前在關注的輸入項。假設你正在閱讀一段文字&#xff0c;這就像你當前在讀的句子。 Key&#xff08;鍵向量&#xff09;&#xff1a;表示其他所有輸入項的標識…

保姆級 | MySQL的安裝配置教程(非常詳細)

一、下載Mysql 官網步驟 MySQLhttps://www.mysql.com/進入官網首頁 點擊DOWNLOADS 點擊MySQL Community (GPL) Downloads 點擊 小頁面直接進入 MySQL :: Download MySQL Installerhttps://dev.mysql.com/downloads/installer/點擊“Download”下載最新版本&#xff0c;其他…

【吊打面試官系列】MySQL 中 InnoDB 支持的四種事務隔離級別名稱,以及逐級之間的區別?

大家好&#xff0c;我是鋒哥。今天分享關于 【MySQL 中 InnoDB 支持的四種事務隔離級別名稱&#xff0c;以及逐級之間的區別&#xff1f;】面試題&#xff0c;希望對大家有幫助&#xff1b; MySQL 中 InnoDB 支持的四種事務隔離級別名稱&#xff0c;以及逐級之間的區別&#xf…

碳素鋼化學成分分析 螺紋鋼材質鑒定 鋼材維氏硬度檢測

碳素鋼的品種主要有圓鋼、扁鋼、方鋼等。經冷、熱加工后鋼材的表面不得有裂縫、結疤、夾雜、折疊和發紋等缺陷。尺寸和允許公差必須符合相應品種國家標準的要求。 具體分類、按化學成分分類 &#xff1a; 碳素鋼按化學成分&#xff08;即以含碳量&#xff09;可分為低碳鋼、中…

機器學習筆記 - stable diffusion web-ui安裝教程

一、Stable Diffusion WEB UI 屌絲勁發作了,所以本地調試了Stable Diffusion之后,就去看了一下Stable Diffusion WEB UI,網絡上各種打包套件什么的好像很火。國內的也就這個層次了,老外搞創新,國內跟著屁股后面搞搞應用層,就叫大神了。 不扯閑篇了,我們這里從git源碼直接…

問題:11單位內部人員對行政機關作出的行政處分不服,可申請行政復議. #其他#微信

問題&#xff1a;11單位內部人員對行政機關作出的行政處分不服,可申請行政復議. 參考答案如圖所示

問題:脾梗塞時,下列情況最符合的是 #職場發展#知識分享#媒體

問題&#xff1a;脾梗塞時,下列情況最符合的是 A、脾腫大 B、脾區摩擦感 C、兩者均有 D、兩者均無 參考答案如圖所示

uniapp視頻組件層級太高,解決方法使用subNvue原生子體窗口

目錄 前言 先看一下uniapp官網的原話&#xff1a; subNvue的一些參數介紹 subNvues使用方法&#xff1a; 綁定id 顯示 subNvue 彈出層 subNvue.show() 參數信息 subNvue.hide() 參數信息 在使用subNvue 原生子體窗口 遇到的一些問題 前言 nvue 兼容性 以及使用方式 控…

基于 中間件 的 數據交換平臺 的實現

一、介紹 A. 背景和目的 隨著云計算、大數據和物聯網等技術的快速發展&#xff0c;企業面臨著越來越多的數據交換和集成需求。不同系統之間的數據交換變得越來越復雜&#xff0c;而且數據量也越來越大&#xff0c;這對傳統的數據交換方式提出了更高的要求。 中間件作為一種能…

把ROS程序作為桌面圖標雙擊啟動

1 寫launch文件 把ROS程序寫成一個launch文件&#xff0c;例如 powerline_with_rviz.launch <launch><!-- Load camera parameters --><rosparam file"$(find choose_powerline)/config/camera_params.yaml" command"load"/><!-- …

深入理解并應用KTT求解約束性極值問題

KT 很簡單&#xff0c;口訣記心端&#xff0c;等式求最優&#xff0c;不等式驗證——小飛打油 以后每期嘗試編一句口訣&#xff0c;幫助大家記憶&#xff0c;可以是打油詩&#xff0c;也可以是類似“奇變偶不變&#xff0c;符號看象限”的口訣&#xff0c;如果編的不好&#xf…

2024年6月7日第十五周下午學習英語六級大綱

下午學習英語六級大綱的內容可以歸納為以下幾個主要方面&#xff1a; 一、考試概述 六級考試的對象&#xff1a;修完大學英語相應階段課程的在校大學生。考試目的&#xff1a;參照《大學英語教學指南》設定的教學目標&#xff0c;對我國大學生英語綜合運用能力進行科學測量&a…

Docker 常用命令以及鏡像選擇

目錄 1.Docker基本組成 2.鏡像選擇 2.1、鏡像推薦選擇方案 2.2版本選擇 3.Docker 命令 3.1鏡像管理 拉取鏡像&#xff1a; 列出鏡像&#xff1a; 刪除鏡像&#xff1a; 構建鏡像&#xff1a; 3.2容器管理 運行容器 列出運行中的容器和所有容器 停止容器 啟動重啟…

【Qt】QPushButton 與 QAction 的區別

1. QPushButton QPushButton 是一個界面控件&#xff0c;能顯示到界面上的。QPushButton 是 QWidget的一個子類&#xff0c;是一個表示按鈕的界面控件。它用于在GUI中提供一個標準的按鈕&#xff0c;用戶可以點擊它來觸發一個即時的動作或命令。按鈕可以顯示文本、圖標或兩者都…

為什么要將Modbus轉成MQTT

什么是Modbus Modbus 是一種串行通信協議&#xff0c;最初由Modicon&#xff08;現在的施耐德電氣Schneider Electric&#xff09;于1979年開發&#xff0c;用于可編程邏輯控制器&#xff08;PLC&#xff09;之間的通信。Modbus協議設計簡單&#xff0c;易于部署和維護&#xf…

從零入手人工智能(2)——搭建開發環境

1.前言 作為一名單片機工程師&#xff0c;想要轉型到人工智能開發領域的道路確實充滿了挑戰與未知。記得當我剛開始這段旅程時&#xff0c;心中充滿了迷茫和困惑。面對全新的領域&#xff0c;我既不清楚如何入手&#xff0c;也不知道能用人工智能干什么。正是這些迷茫和困惑&a…