C/C++ 實現在快速排序Quick Sort中的三種分區方式

1. 簡介

神說, 要有光. 于是就有了光. 神說要有快排, 于是就有了快排.?

快速排序Quick Sort的發明者 托尼 霍爾 是1980年的圖靈獎得主. 快速排序就是他發明的. 當時發明的背景是: 由于霍爾要高效地對俄語詞匯進行排序以優化翻譯程序, 而當時的排序算法(如冒泡, 插入排序)效率較低, 平均時間復雜度為 O(n2). 霍爾受分治法 (Divide and Conquer)思想啟發, 提出了基于 "分區" (Partition) 的遞歸排序方法. (這段AI獲取)

2. 核心思想

分區 (Partition): 指的是選一個基準值 Pivot, 將比基準值小的放在它的左側, 比它大的放在右側. 然后, 分別在左邊區域 (比基準值小的區域) 和右邊區域 (比基準值大的區域) 做相同的操作即再在左右兩邊選基準值, 再劃分為兩個區域, 一直到劃分的區域即子序列長度為1, 即只有一個元素為止.這個就是快速排序的核心思想.

3.算法實現方式

分區法據我了解到有三種:? ?(因為很早前寫的,細節解釋可能有誤, 注意甄別)

3.1 第一種是 Naive Partition (樸素分區法)

這種方法我用 C++ 實現過, 不過我是看到某個Python的實現, 我根據它的實現, 用 C++ 翻譯而已. 就是我們選一個基準值, 然后遍歷元素, 比它小的我們額外存放到一個臨時空間中, 比它大的也一樣. 然后再將臨時空間中的比基準值小的搬回原來的存儲空間中, 再放基準值, 然后把比基準值大的放到基準值的右邊. 再分別遞歸處理基準值左邊和右邊的區域. 如果某一輪沒有找到比基準值小或大的值, 則左邊或右邊不再遞歸處理.

3.2 第二種是 Lomuto Partition (洛穆托分區法)?

它維護兩個指針 (下標), i 和 j . 依次從最左邊的元素遍歷到最末元素的前一個 (倒數第二個) , 并且選擇最末元素為基準值. i 初始值指向 -1, j 指向第一個元素, 如果當前 j 指向的元素比基準值小, 則增加 i 的值, 并交換 i 和 j 指向的元素, 也就是將比基準值小的元素往后擺放. 如果 j 指向的元素比基準值大, 則不動它.? i 增加只在發生交換時增加, j 是每次無論交換與否都要增加.最終 i + 1 的位置就是返回的基準值的位置, 再遞歸處理 i + 1左邊和右邊.

3.3 第三種是 Hoare Partition (霍爾分區法)

這也是快排原始版本, 由霍爾本人發明, 好像也是最快的實現方式. 它也維護兩個下標 low?和 high, 只是從元素序列的兩邊分別遍歷, 一個往后, 一個往前. 并且一般選第一個元素為基準值. 往后移的下標 low 找到比基準值大于等于的值, 往前移的 high 下標找比基準值小于等于的值, 找到則停止, 此時如果 low 和 high 還沒相遇, 則交換它倆. 即將大的往后放, 小的往前放. 若相遇則返回 high下標. 這個 high 下標的位置就是基準值的位置. 然后遞歸處理基準值左邊和右邊.

(如果文字抽象或這里沒寫正確, 可以看實現代碼在紙上推演, 推兩輪自然明白, 我這里就不畫圖了, 畫圖有點耗時間)

4. 代碼實現

4.1 第一種樸素分區法

注釋也比較詳細, 思想就是剛剛介紹的, std::array 模版類是 C++ 中的一種數組/容器, std::vector可能更適合長度經常變化的數組, std::array 數組長度固定, 效率比 std::vector 高, 而 std::array 又封裝了一些額外的功能 (什么邊界檢查,還有一些方法) 比原始數組好用. ( C++的幾種數組效率可以用百萬級的元素或千萬級的元素來測試) . 這個版本雖不能完全算原創, 但也是半原創, 就是根據某個Python實現, 用 C++ 獨立完成.

/*快速排序 樸素分區法 ?? C++版 08042024 by losangelx*/
#include <iostream>
#include <array>
const int SIZE = 10;/*快速排序函數,start為排序起始下標,len為要排序的范圍*/
bool quickSort(std::array<int, SIZE>& ar, int start, int len);int main(void)
{std::array<int, SIZE> numbers = { 0, 422, 12, 4, 111, 3, 5, 22, 200, 123 };std::array<int, SIZE>::iterator ir; /*array模版類迭代器*/std::cout << "The oringal array: "; /*原始數組*/for (ir = numbers.begin(); ir != numbers.end(); ir++)std::cout << *ir << " ";quickSort(numbers, 0, numbers.size());std::cout << "\nAfter quick sorted: "; /*排序后數組*/for (int x : numbers)std::cout << x << " ";return 0;
}/*快速排序函數,start為排序起始下標,len為要排序的范圍*/
bool quickSort(std::array<int, SIZE>& ar, int start, int len)
{if (len == 1) //基線條件:如果長度為1不再遞歸排序return 1;std::array<int, SIZE> small; /*臨時元素存放區*/std::array<int, SIZE> larger;/*small為比基準因子小的元素*/int i, j, k;                 /*larger為比基準因子大的元素*//*將原數組中比基準因子小和大的先搬出來*/for (i = start + 1, j = 0, k = 0; i - start < len; i++)if (ar[i] <= ar[start])small[j++] = ar[i];elselarger[k++] = ar[i];/*基準因子arr[start]最終排在由比它小的元素組成的子*//*數組之后并且在比它大的元素組成的子數組之前的位置上*/ar[start + j] = ar[start];/*如果有比基準因子小的元素則搬回原數組,并對子*//*數組(由比基準因子小的組成)執行相同操作(遞歸)*/if (j > 0) {int m = 0;for (i = start; i - start < j; i++)ar[i] = small[m++];quickSort(ar, start, j);//只對長度為j的部分排序}                           //即只對原數組比基準因子//小的那部分執行相同操作/*若有比基準因子大的元素則執行相同操作*/if (k > 0) {int n = 0;for (i = start + j + 1; (i - (start + j + 1)) < k; i++)ar[i] = larger[n++];quickSort(ar, start+j+1, k);}return 1; /*排序完成返回*/
}

我用 Dev C++ 編譯這個代碼遇到下面錯誤, 這里是要在dev c++中添加?C++ 11 支持才行, std::array是 C++ 11 才引入的. 如果你用 VS 應該不用.

#ifndef _CXX0X_WARNING_H
#define _CXX0X_WARNING_H 1

#if __cplusplus < 201103L
#error This file requires compiler and library support for the \
ISO C++ 2011 standard. This support is currently experimental, and must be \
enabled with the -std=c++11 or -std=gnu++11 compiler options.
#endif

#endif

4.1.1 運行結果:

4.2 第二種和第三種 Lomuto 分區法和 Hoare 分區法

/* Quick Sort 快速排序 本程序使用Lomuto/Hoare兩種分區法實現 */
#include <stdio.h>
#define MAX 10void quicksort(long arr[], int low, int high);    /* 快速排序主函數 */
int hoare_parti(long arr[], int low, int high);   /* 霍爾分區法     */
int lomuto_parti(long arr[], int low, int high);  /* 洛穆托分區法   */
void swap(long *pl, long *ph);
void printarr(long arr[], int len);
int main(void)
{long array[MAX] = {50, 80, 10, 70, 100,30, 90, 40, 60, 20};printarr(array, MAX);printf("\n");quicksort(array, 0, MAX - 1);printarr(array, MAX);printf("\n");return 0;
}void quicksort(long arr[], int low, int high)
{if (low < high) {  /* 遞歸基線條件: low與high還沒相遇才繼續 */int pindex = lomuto_parti(arr, low, high);  /* 這里可選Hoare分區法或洛穆托分區法 */quicksort(arr, low, pindex - 1);     /* 遞歸處理左右兩邊的序列 */quicksort(arr, pindex + 1, high);}
}/* Hoare分區法 */
int hoare_parti(long arr[], int low, int high)
{long pivot = arr[low];                      /* 選第一個為基準值 */while (1) {while (arr[low] < pivot) { low++; }     /* 直到找到比基準值大的停下 */while (arr[high] > pivot) { high--; }   /* 直到找到比基準值小的停下 */if (low >= high) { return high; }       /* low和high沒相遇繼續否則返回基準值位置 */swap(&arr[low], &arr[high]);            /* 交換比基準值大的到后面反之放前面 */}
}/* Lomuto分區法 */
int lomuto_parti(long arr[], int low, int high)
{int i, j;long pivot = arr[high];                     /* 選最后一個元素為基準值 */i = low - 1;for (j = low; j < high; j++) {              /* i開始指向比基準值小的后一個 */if (arr[j] <= pivot) {                  /* j從第一個開始, 若小于等于基準值 */i++;swap(&arr[i], &arr[j]);             /* 則交換即將小的往前放,大的往后放 */}}swap(&arr[i + 1], &arr[high]);              /* 將基準值放到適當位置 */return (i + 1);                             /* 返回基準值位置 */
}void printarr(long arr[], int len)
{int i;for (i = 0; i < len; i++) {printf("%ld ", arr[i]);}
}void swap(long *pl, long *ph)
{long temp = *pl;*pl = *ph;*ph = temp;
}

上面兩種分區法都測試通過了. 不過如果Dev C++編譯選項還有 C++ 11的話, 會有警告, 應該關閉.因為這是 C. (C 和 C++ 有時候一樣, 但有些特性還是不太一樣, C++ 復雜一些,據大老C++之父本賈尼的說法是 C 的語法檢查太松散. 所以C++檢查也更嚴格)

4.2.1 運行結果:

注意: 以上的三種實現方式代碼如果有錯誤注意甄別, 我并未仔細測試, 通過即停.

5. 結語

快速排序Quick Sort 還有多種優化方式, 感興趣的讀者可以研究一下. 例如基準值的選擇就有多種不同的算法, 比如選第一個和中間, 還有最后一個元素三個取其一等等. 優化空間很大. 如果這篇文章缺少圖示, 就自己在紙上根據代碼推演, 推兩盤就立刻明白是如何實現排序的了.

關于快速排序詳細的時間復雜度分析請搜相關文章了解,不同分區方式它的時間復雜度有些區別,當然樸素分區因為要遞歸創建臨時數組并且要搬運,所以是最慢的。而洛穆托和霍爾分區法因為是在原始數組上處理不用創建臨時空間和搬運要快點,霍爾分區法因為是從兩頭往中間同時雙向遍歷所以最快,比洛穆托的要快。

這里我也人云亦云的給岀快排平均時間復雜度函數是 n * log n,其中 n 是元素個數。這個函數是對數,隨著處理元素數量的增加,函數值增量趨勢沒有n * n 大,也就是說比什么冒泡,插入排序的n * n 要快,花費的時間更少,就是說快排的n*log n讓計算機干的活兒的量比n * n的算法要少。至于如何得岀n * log n是前面也說了去搜相關文章,網上很多,都是分析要干多少活兒得岀的數學函數。

我實在無法理解為什么要把time complexity直譯成時間復雜度這個抽象詞匯,這個直接翻譯為性能不行嗎,反正只是衡量算法性能的一個數學函數。也許計算機科學家和數學家翻譯有講究吧。

喜歡就點贊收藏, 點贊收藏是我創作的動力.

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

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

相關文章

Flink TiDB CDC 環境配置與驗證

一、TiDB 數據庫核心配置 1. 啟用 TiCDC 服務 確保 TiDB 集群已部署 TiCDC 組件&#xff08;版本需兼容 Flink CDC 3.0.1&#xff09;&#xff0c;并啟動同步服務&#xff1a; # 示例&#xff1a;啟動 TiCDC 捕獲 changefeed cdc cli changefeed create \--pd"localhos…

2025年數據挖掘與計算機科學國際會議 (DMCS 2025)

2025 International Conference on Data Mining and Computer Science【一】、大會信息 會議簡稱&#xff1a;DMCS 2025 大會地點&#xff1a;中國廣州 收錄檢索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等【二】會議簡介2025年數…

騰訊輕量云和云服務器的區別

從問題本身來看&#xff0c;用戶應該對云計算有基本了解&#xff0c;但可能不太清楚騰訊云產品線的細分定位。這類問題通常出現在項目初期技術選型階段&#xff0c;用戶需要權衡成本和性能。 讓我先梳理兩者的核心差異點。輕量云本質是面向輕量級應用的打包解決方案&#xff0c…

在使用ffmpeg時遇到了復制路徑在終端輸入指令后,報錯的解決方法

錯誤如下所示&#xff1a;解決方法&#xff1a;??檢查路徑中的特殊字符??&#xff1a;你的路徑中包含了一個不可見的Unicode字符&#xff08;?&#xff0c;即LEFT-TO-RIGHT MARK&#xff09;&#xff0c;這是從網頁復制路徑時常見的隱藏字符??解決方案??&#xff1a;直…

高頻變壓器材料新解:納米晶的渦流損耗逆襲之路

通過帶材做薄納米晶&#xff0c;可以降低渦流損耗。原因有二&#xff1a;一、納米晶做薄可以減小磁場的趨膚效應&#xff1b;二、納米晶越薄材料電阻越高&#xff0c;整體電阻越大&#xff0c;渦流損耗越小。本篇&#xff0c;就來詳細談談變壓器的渦流損耗。 鐵氧體材料成本低&…

DMA技術與音頻數據的存儲和播放

基本概念 采樣率: 每秒采集的采樣點次數。如480000HZ, 就是我們常見的48KHZ采樣點(Sample):每一個采樣點代表一個時間點的聲音幅度值。對于立體聲,每個采樣點包含了兩個聲道(左聲道,右聲道)的數據。幀:一幀就是一個時刻采集的數據,如果音頻是立體聲則會產生2個采樣點,如…

項目進度受外包團隊影響,如何管控交付節奏

項目進度受外包團隊影響時&#xff0c;管控交付節奏的關鍵措施包括明確交付標準與節點、建立可視化進度監控機制、強化合同約束與激勵條款、保持高頻溝通與快速響應機制、建立聯合質量審查機制。其中&#xff0c;明確交付標準與節點最為關鍵。通過制定具體、可量化的交付標準與…

BM9 刪除鏈表的倒數第n個節點

目錄 題目鏈接 題目 解題思路 代碼 題目鏈接 刪除鏈表的倒數第n個節點_牛客題霸_牛客網 題目 解題思路 先利用快慢指針找到刪除位置的前一個節點,然后進行刪除即可(具體就是快指針先移動n1個,因為要找到刪除指針的前一個節點) 代碼 import java.util.*;/** public clas…

java中ehcache因為可以緩存到本地,假如生產環境使用ehcache是不是需要在生產環境服務器創建緩存文件夾目錄以存儲ehcache緩存的數據

是的&#xff0c;當在生產環境中使用 Ehcache 的磁盤持久化功能時&#xff0c;確實需要在服務器上創建相應的緩存文件夾目錄&#xff0c;并確保應用程序有權限讀寫該目錄。 以下是詳細說明和配置建議&#xff1a;1. 為什么需要創建緩存目錄&#xff1f;Ehcache 的磁盤持久化功能…

day55

1. 序列預測介紹序列預測就是根據過去的序列數據&#xff08;比如時間順序的數據&#xff09;&#xff0c;預測未來的結果。? 單步預測&#xff1a;只預測下一個時刻的值。比如根據前7天的氣溫&#xff0c;只預測第8天的氣溫。? 多步預測的2種方式&#xff1a;? 遞歸式&…

javaweb———html

我才開始學javaweb&#xff08;重點不在這&#xff09;可能學的比較慢&#xff0c;勿說HTML 基礎結構HTML 文檔的基本結構包含 <!DOCTYPE html> 聲明、<html> 根元素、<head> 頭部和 <body> 主體部分。<head> 中包含頁面元信息&#xff0c;如標題…

OpenCV在Visual Studio 2022下的配置

OpenCV是一個開源的計算機視覺和機器學習軟件庫&#xff0c;廣泛應用于圖像處理、目標檢測、模式識別等領域。它通常搭配在Visual Studio集成開發環境中使用&#xff0c;配置步驟主要有下載安裝、加入系統環境變量、設置VS項目屬性等。 1. 下載安裝 a) 進入OpenCV官網&#xf…

kafka如何讓消息均勻的寫入到每個partition

在Kafka中,要實現消息均勻寫入每個partition,核心是通過合理的分區分配策略讓消息在partition間均衡分布。具體機制和實踐方式如下: 一、Kafka默認的分區分配邏輯(核心機制) Kafka生產者發送消息時,通過Partitioner接口(默認實現為DefaultPartitioner)決定消息寫入哪…

centos7修改yum源并安裝Ansible

1、修改yum源在 CentOS 系統中&#xff0c;將默認的 yum 源修改為阿里云的鏡像源&#xff0c;可以加快軟件包的下載速度。以下是詳細步驟&#xff1a;1&#xff09;備份原有的 yum 源配置sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup2…

Ubuntu 25.04安裝搜狗輸入法

0x00 安裝思路 1. 卸載 ibus 和 fcitx5。 # 更新系統軟件包 sudo apt update# 卸載 Fcitx5 和 IBus&#xff08;如果存在&#xff09; sudo apt remove --purge fcitx5* ibus*# 清理系統殘留 sudo apt autoremove && sudo apt autoclean 2. 安裝fcitx4。 # 安裝 Fc…

二、Docker安裝部署教程

作者&#xff1a;IvanCodes 日期&#xff1a;2025年7月7日 專欄&#xff1a;Docker教程 在前一篇文章中&#xff0c;我們了解了 Docker 的歷史、能做什么以及核心概念 (鏡像、容器、倉庫)。現在&#xff0c;我們將更進一步&#xff0c;深入探究 Docker 中最常用也最核心的命令—…

【debug】git clone 報錯

報錯如下&#xff1a; error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err 8) error: 1964 bytes of body are still expected fetch-pack: unexpected disconnect while reading sideband packet fatal: early EOF fatal: fetch-pack: invalid…

二、MySQL 8.0 之《場景分析:不犧牲數據完整性下提供最大性能改進》

文章目錄前言一、場景二、場景問題分析正確的四項選擇 (B, C, E, H)錯誤的五項選擇 (A, D, F, G, I)三、場景問題收獲1. MySQL I/O子系統優化 (I/O Subsystem Optimization)2. InnoDB存儲引擎關鍵參數調優 (InnoDB Key Parameter Tuning)3. 數據完整性與ACID特性 (Data Integri…

Nuxt.js 靜態生成中的跨域問題解決方案

當您運行 npm run generate 生成靜態頁面時&#xff0c;Vite 的代理服務器確實無法使用&#xff0c;因為生成階段是在 Node.js 環境中執行的構建過程。但別擔心&#xff0c;我將為您提供一套完整的解決方案來處理構建階段的跨域問題。核心解決方案1. 構建階段&#xff1a;使用服…

【AI總結】Git vs GitHub vs GitLab:深度解析三者聯系與核心區別

目錄1 Git&#xff1a;版本控制的核心引擎1.1 Git的核心架構與工作原理1.2 Git的工作流程與區域劃分1.3 Git的核心能力2 GitHub vs GitLab&#xff1a;云端雙雄的差異化定位2.1 核心定位與市場策略2.2 技術架構深度對比2.2.1 核心功能差異2.2.2 AI能力演進路線&#xff08;2025…