快速排序 C++

快速排序 C++

本文圖示借鑒自清華大學鄧俊輝老師數據結構課程。

快速排序的思想

快速排序是分治思想的典型應用。該排序算法可以原地實現,即空間復雜度為 O(1)O(1)O(1),而時間復雜度為 O(nlogn)O(nlogn)O(nlogn)

算法將待排序的序列 SSS 分為兩個子序列 SLS_LSL?SRS_RSR? ,隨著這種子序列的劃分,可以保證問題的規模在不斷縮小,即有:
max(∣SL∣,∣SR∣)<nmax(|S_L|,|S_R|) < n max(SL?,SR?)<n
并要求左側子序列 SLS_LSL? 中的最大值不大于右側子序列 SRS_RSR? 中的最小值,即有:
max(SL)≤min(SR)max(S_L)\le min(S_R) max(SL?)min(SR?)
這樣在遞歸地再對子序列進行劃分之后,原序列自然有序。遞歸的終止條件為劃分子序列的平凡情況,即子序列的規模為1。

在這里插入圖片描述

我們將劃分兩個子序列的點稱為 pivot(軸點) ,在軸點左側的元素,均不比它更大;在軸點右側的元素,均不比它更小。軸點的位置,即為該元素在最終排序完成的序列中的位置。

即將原序列按如下劃分:
[l,r]=[l,pivot)+pivot+(pivot,r][\ l, r\ ]=[\ l, pivot\ )+pivot+(\ pivot,r\ ] [?l,r?]=[?l,pivot?)+pivot+(?pivot,r?]
但是對于待排序的序列,我們并不知道誰是軸點,甚至,有可能原序列中所有元素都不是軸點,比如將一個已排序的序列循環右移一位,則此時所有元素都不在自己的排序位置上,都不是軸點。

好在我們可以通過對待排序的序列的元素進行移動交換,從而使得某個元素成為軸點,在遞歸之后,所有元素都成為軸點,這時所有元素都位于自己的排序位置上,整個排序算法完成。

可以看到,整個算法的框架是遞歸地選取軸點并劃分子序列,究竟怎樣劃分,怎樣劃分效率更高,是快排算法的關鍵之處。

算法框架

我們先給出整個快排算法的遞歸框架,其中關鍵的 partition 劃分函數我們會在后面詳細介紹,整體框架是一個遞歸函數:

void quickSort(vector<int>& vec, int l, int r) {if (l<r) {int pivot = partition(vec, l, r);quickSort(vec, l, pivot-1);quickSort(vec, pivot+1, r);}
}

當 l 不小于 r 時,即 l 已經等于 r,此時即為遞歸退出的平凡情況:子序列只有一個元素。在此之前,我們都需要不斷地執行 if 語句中的內容:得到傳入序列的一個軸點,并分別遞歸地處理該軸點兩側的子序列。

最終當傳入的子序列規模為1是,即 l<r 的判斷條件不成立,達到返回條件,整個遞歸過程開始返回。

partition

版本1

接下來我們來介紹最為關鍵的 partition 函數。

在這里插入圖片描述

我們先任選一個元素作為軸點的 “培養對象” 并備份,在我們劃分完畢之后,該元素可以 “名正言順” 地位于自己應該處于的最終排序位置,不妨就取序列的最左端元素。

然后將兩個指針分別放在整個序列的左右兩端,分別向中間靠攏。

我們將

  • 未確定區域稱為 UUU (圖中粉色部分),初始為全集
  • 確定大于軸點的部分稱為 GGG (圖中藍色部分),初始為空集
  • 確定小于軸點的部分稱為 LLL (圖中綠色部分),初始為空集

交替地將左右兩端的指針向中間移動,分別檢查移動時當前元素與軸點 pivot 的大小關系,若小于軸點,則歸入 LLL;若大于軸點,則歸入 RRR

當左右兩指針相等時,該位置即為我們的 “培養對象” 軸點應該處于的排序位置,將備份的軸點放入該位置即可。最后同樣返回軸點位置。

整個過程中,左右指針所指的位置交替空閑。所謂空閑即是指其中元素可以被覆蓋。一開始時,我們將 ”培養對象“ 備份,故其位置是 “空閑” 的,在隨后的交還過程中,左右指針總有一個所指的位置是空閑的。

在這里插入圖片描述

可以參考上圖,圖中紫色部分即為 “空閑” 的。紅色的 lo、hi 分別對應我們描述中的左右指針。

代碼實現如下:

int partition(vector<int>& vec, int l, int r) {int pivot = vec[l];while(l < r) {while (l<r && pivot<=vec[r]) r--;swap(vec[r], vec[l]);while (l<r && pivot>=vec[l]) l++;swap(vec[r], vec[l]);}vec[l] = pivot;return l;
}

版本2

對于 partition 函數,還有一種實現的方式,可以稱為 LGU 的方式,在這種方式中,L 仍舊排在原序列的最左端,而中間是 G ,最右端是 U。

在這里插入圖片描述

同樣可以選取最左端的元素作為 pivot 。

然后一根指針從序列的最左端遍歷到最右端,分別判斷每個當前元素與 pivot 的關系,并分別劃分到 L 或 G 中。

如果是劃分到 G 中,直接將 k++ 即可;而如果是劃分到 L 中,則需要將 G 滾動后移一個元素,并將當前元素交換到 L 的末尾。

代碼實現如下:

int partition(vector<int>& vec, int l, int r) {int m = l, pivot = vec[l];for (int k=l+1; k<=r; k++) {if (pivot > vec[k])swap(vec[++m], vec[k]);}swap(vec[l], vec[m]);return m;
}

這里中間判斷中的 if 分支是判斷當前元素小于軸點 pivot ,需要將當前元素加入到 L 中,將當前元素 vec[k] 與 L 的最后一個元素 vec[++m] 互換,然后k++;而它對應的 else 分支,即當前元素小于軸點 pivot,需要將當前元素加入到 G 中,此時只需將 k++。兩個分支都需要 k++,直接放在循環變量的更新中。

最后同樣返回軸點位置。

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

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

相關文章

Linux命令行下感嘆號的幾個用法

Linux命令行下 " ! " 的幾個用法 ! 在大多數編程語言中表示取反的意思&#xff0c;但是在命令行中&#xff0c;他還有一些其他的神奇用法。熟練掌握這些用法&#xff0c;可以大大提高我們日常命令行操作的效率。 1 執行歷史命令 !! ! 在命令行中可以用來執行歷史…

三地址碼簡介

三地址碼簡介 三地址碼&#xff08;Three Address Code&#xff09;是一種最常用的中間語言&#xff0c;編譯器可以通過它來改進代碼轉換效率。每個三地址碼指令&#xff0c;都可以被分解為一個四元組&#xff08;4-tuple&#xff09;的形式&#xff1a;&#xff08;運算符&am…

llvm與gcc

llvm與gcc llvm 是一個編譯器&#xff0c;也是一個編譯器架構&#xff0c;是一系列編譯工具&#xff0c;也是一個編譯器工具鏈&#xff0c;開源 C11 實現。 gcc 相對于 clang 的優勢&#xff1a; gcc 支持更過語言前端&#xff0c;如 Java, Ada, FORTRAN, Go等gcc 支持更多地 …

攻防世界web新手區解題 view_source / robots / backup

1**. view_source** 題目描述&#xff1a;X老師讓小寧同學查看一個網頁的源代碼&#xff0c;但小寧同學發現鼠標右鍵好像不管用了。 f12查看源碼即可發現flag 2. robots 題目描述&#xff1a;X老師上課講了Robots協議&#xff0c;小寧同學卻上課打了瞌睡&#xff0c;趕緊來教教…

python參數傳遞*args和**kwargs

python參數傳遞*args和**kwargs 和* 實際上真正的Python參數傳遞語法是 * 和 ** 。*args 和 **kwargs 只是一種約定俗成的編程實踐。我們也可以寫成 *vars 和 **kvars 。就如同其他常規變量的命名一樣&#xff0c; args 和 kwargs 只是一種習慣的名稱。 *args 和 **kwargs 一…

聽GPT 講Rust源代碼--src/tools(25)

File: rust/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs 在Rust源代碼中&#xff0c;suspicious_command_arg_space.rs文件位于clippy_lints工具包的methods目錄下&#xff0c;用于實現Clippy lint SUSPICIOUS_COMMAND_ARG_SPACE。 Clippy是Ru…

Java一次編譯,到處運行是如何實現的

Java一次編譯&#xff0c;到處運行是如何實現的 轉自&#xff1a;https://cloud.tencent.com/developer/article/1415194 &#xff08;排版微調&#xff09; JAVA編譯運行總覽 Java是一種高級語言&#xff0c;要讓計算機執行你撰寫的Java程序&#xff0c;也得通過編譯程序的…

JIT(動態編譯)和AOT(靜態編譯)編譯技術比較

JIT&#xff08;動態編譯&#xff09;和AOT&#xff08;靜態編譯&#xff09;編譯技術比較 轉自&#xff1a;https://www.cnblogs.com/tinytiny/p/3200448.html Java 應用程序的性能經常成為開發社區中的討論熱點。因為該語言的設計初衷是使用解釋的方式支持應用程序的可移植…

python解釋器

python解釋器 計算機編程語言 本部分參考自&#xff1a;https://zhuanlan.zhihu.com/p/141212114 從計算機編程語言說起&#xff0c;它主要分為三類&#xff1a;機器語言、匯編語言、高級語言。 機器語言是一種計算機可以直接識別并執行的二進制指令集。由于其可以直接交給…

編譯型語言與解釋型語言

編譯型語言與解釋型語言 首先要說明&#xff0c;編譯型語言與解釋型語言這種分類方法是不科學的&#xff0c;或者說已經過時了&#xff0c;但是這種稱呼大抵還是能夠讓人明白我們將要討論的是什么東西。 文中所列參考是筆者認為比較有幫助的一些擴展閱讀內容。 首先貼一個很形…

常見的各種shell及其區別

常見的各種shell及其區別 引子 for((i1;i<10;i)); do echo $(expr $i \* 3 1); done 網上搜到的 shell for循環腳本&#xff0c;別人都能正常運行&#xff0c;我卻報錯&#xff1a; Syntax error: Bad for loop variable究竟是怎么回事呢&#xff1f; shell簡介…

shell腳本 變量

shell腳本 變量類型 什么是Shell變量 用一個固定的字符串去表示不固定的內容。 Shell變量的類型 shell腳本中自定義變量的類型&#xff0c;我們這里分為&#xff1a; 自定義變量環境變量位置變量與定義變量 這四類&#xff0c;它們有一些相同點&#xff0c;但又有些不同點…

攻防世界web新手區解題 /cookie / disabled_button / weak_auth

cookie 題目描述&#xff1a;X老師告訴小寧他在cookie里放了些東西&#xff0c;小寧疑惑地想&#xff1a;‘這是夾心餅干的意思嗎&#xff1f;’ 使用burp suite抓包查看 發現提示&#xff1a; look-herecookie.php 于是在url后加上 cookie.php 得到提示查看返回 就得到了f…

Python 函數式編程

Python 函數式編程 轉自&#xff1a;https://www.liaoxuefeng.com/wiki/1016959663602400/1017328525009056&#xff0c;推薦去該鏈接讀原文&#xff0c;有習題和熱烈的評論區交流。 函數式編程 函數是Python內建支持的一種封裝&#xff0c;我們通過把大段代碼拆成函數&…

Python中的生成器與迭代器

Python中的生成器與迭代器 轉自&#xff1a;https://www.liaoxuefeng.com/wiki/1016959663602400/1017323698112640&#xff0c;推薦去該鏈接讀原文&#xff0c;有習題和熱烈的評論區交流。 生成器 通過列表生成式&#xff0c;我們可以直接創建一個列表。但是&#xff0c;受…

基于GET報錯的sql注入,sqli-lab 1~4

根據注入類型可將sql注入分為兩類&#xff1a;數字型和字符型 例如&#xff1a; 數字型&#xff1a; sleect * from table where if 用戶輸入id 字符型&#xff1a;select * from table where id 用戶輸入id &#xff08;有引號) 通過URL中修改對應的D值&#xff0c;為正常數字…

Python 裝飾器詳解(上)

Python 裝飾器詳解&#xff08;上&#xff09; 轉自&#xff1a;https://blog.csdn.net/qq_27825451/article/details/84396970&#xff0c;博主僅對其中 demo 實現中不適合python3 版本的語法進行修改&#xff0c;并微調了排版&#xff0c;本轉載博客全部例程博主均已親測可行…

xss原理和注入類型

XSS漏洞原理 : XSS又叫CSS(cross Site Script), 跨站腳本攻擊,指的是惡意攻擊者往Web頁面里插入惡意JS代碼,當用戶瀏覽該頁時,嵌入其中的Web里的JS代碼就會被執行,從而達到惡意的特殊目的. 比如:拿到cooike XSS漏洞分類: 反射性(非存儲型) payload沒有經過存儲,后端接收后,直接…

Python 裝飾器詳解(中)

Python 裝飾器詳解&#xff08;中&#xff09; 轉自&#xff1a;https://blog.csdn.net/qq_27825451/article/details/84581272&#xff0c;博主僅對其中 demo 實現中不適合python3 版本的語法進行修改&#xff0c;并微調了排版&#xff0c;本轉載博客全部例程博主均已親測可行…

存儲型xss案例

存儲型xss原理: 攻擊者在頁面插入xss代碼,服務端將數據存入數據庫,當用戶訪問存在xss漏洞的頁面時,服務端從數據庫取出數據展示到頁面上,導致xss代碼執行,達到攻擊效果 案例: 在一個搭建的論壇網站中, 根據存儲型xss注入的條件,要找到可以存儲到數據庫的輸入位置,并且這個位置…