基本介紹
- 中文名:快速排序算法
- 外文名:quick sort
- 別稱:快速排序
- 提出者:C. A. R. Hoare
- 提出時間:1960年
- 套用學科:計算機科學
- 適用領域範圍:Pascal,c++等語言
算法介紹
排序演示
示例
下標 | 0 | 1 | 2 | 3 | 4 | 5 |
數據 | 6 | 2 | 7 | 3 | 8 | 9 |
下標 | 0 | 1 | 2 | 3 | 4 | 5 |
數據 | 3 | 2 | 7 | 6 | 8 | 9 |
下標 | 0 | 1 | 2 | 3 | 4 | 5 |
數據 | 3 | 2 | 6 | 7 | 8 | 9 |
下標 | 0 | 1 | 2 | 3 | 4 | 5 |
數據 | 3 | 2 | 6 | 7 | 8 | 9 |
調用函式
示例代碼
GO
// 第一種寫法func quickSort(values []int, left, right int) { temp := values[left] p := left i, j := left, right for i <= j { for j >= p && values[j] >= temp { j-- } if j >= p { values[p] = values[j] p = j } for i <= p && values[i] <= temp { i++ } if i <= p { values[p] = values[i] p = i } } values[p] = temp if p-left > 1 { quickSort(values, left, p-1) } if right-p > 1 { quickSort(values, p+1, right) }}func QuickSort(values []int) { if len(values) <= 1 { return } quickSort(values, 0, len(values)-1)}// 第二種寫法func Quick2Sort(values []int) { if len(values) <= 1 { return } mid, i := values[0], 1 head, tail := 0, len(values)-1 for head < tail { fmt.Println(values) if values[i] > mid { values[i], values[tail] = values[tail], values[i] tail-- } else { values[i], values[head] = values[head], values[i] head++ i++ } } values[head] = mid Quick2Sort(values[:head]) Quick2Sort(values[head+1:])}// 第三種寫法func Quick3Sort(a []int,left int, right int) { if left >= right { return } explodeIndex := left for i := left + 1; i <= right ; i++ { if a[left] >= a[i]{ //分割位定位++ explodeIndex ++; a[i],a[explodeIndex] = a[explodeIndex],a[i] } } //起始位和分割位 a[left], a[explodeIndex] = a[explodeIndex],a[left] Quick3Sort(a,left,explodeIndex - 1) Quick3Sort(a,explodeIndex + 1,right)}
Ruby
def quick_sort(a) (x=a.pop) ? quick_sort(a.select { |i| i <= x }) + [x] + quick_sort(a.select { |i| i > x }) : []end
Erlang語言
超簡短實現:q_sort([])->[];q_sort([H|R])->q_sort([X||X<-R,X<H])++[H]++q_sort([X||X<-R,X>=H]).
Haskell語言
q_sort n=case n of []->[] (x:xs)->q_sort [a|a<-xs,a<=x]++[x]++q_sort [a|a<-xs,a>x]
C++語言
#include <iostream>using namespace std;void Qsort(int arr[], int low, int high){ if (high <= low) return; int i = low; int j = high + 1; int key = arr[low]; while (true) { /*從左向右找比key大的值*/ while (arr[++i] < key) { if (i == high){ break; } } /*從右向左找比key小的值*/ while (arr[--j] > key) { if (j == low){ break; } } if (i >= j) break; /*交換i,j對應的值*/ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } /*中樞值與j對應值交換*/ int temp = arr[low]; arr[low] = arr[j]; arr[j] = temp; Qsort(arr, low, j - 1); Qsort(arr, j + 1, high);}int main(){ int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24}; Qsort(a, 0, sizeof(a) / sizeof(a[0]) - 1);/*這裡原文第三個參數要減1否則記憶體越界*/ for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++) { cout << a[i] << ""; } return 0;}/*參考數據結構p274(清華大學出版社,嚴蔚敏)*/
C語言版本
void sort(int *a, int left, int right){ if(left >= right)/*如果左邊索引大於或者等於右邊的索引就代表已經整理完成一個組了*/ { return ; } int i = left; int j = right; int key = a[left]; while(i < j) /*控制在當組內尋找一遍*/ { while(i < j && key <= a[j]) /*而尋找結束的條件就是,1,找到一個小於或者大於key的數(大於或小於取決於你想升 序還是降序)2,沒有符合條件1的,並且i與j的大小沒有反轉*/ { j--;/*向前尋找*/ } a[i] = a[j]; /*找到一個這樣的數後就把它賦給前面的被拿走的i的值(如果第一次循環且key是 a[left],那么就是給key)*/ while(i < j && key >= a[i]) /*這是i在當組內向前尋找,同上,不過注意與key的大小關係停止循環和上面相反, 因為排序思想是把數往兩邊扔,所以左右兩邊的數大小與key的關係相反*/ { i++; } a[j] = a[i]; } a[i] = key;/*當在當組內找完一遍以後就把中間數key回歸*/ sort(a, left, i - 1);/*最後用同樣的方式對分出來的左邊的小組進行同上的做法*/ sort(a, i + 1, right);/*用同樣的方式對分出來的右邊的小組進行同上的做法*/ /*當然最後可能會出現很多分左右,直到每一組的i = j 為止*/}
JavaScript
const quickSort = (array) => { const sort = (arr, left = 0, right = arr.length - 1) => { if (left >= right) {//如果左邊的索引大於等於右邊的索引說明整理完畢 return } let i = left let j = right const baseVal = arr[j] // 取無序數組最後一個數為基準值 while (i < j) {//把所有比基準值小的數放在左邊大的數放在右邊 while (i < j && arr[i] <= baseVal) { //找到一個比基準值大的數交換 i++ } arr[j] = arr[i] // 將較大的值放在右邊如果沒有比基準值大的數就是將自己賦值給自己(i 等於 j) while (j > i && arr[j] >= baseVal) { //找到一個比基準值小的數交換 j-- } arr[i] = arr[j] // 將較小的值放在左邊如果沒有找到比基準值小的數就是將自己賦值給自己(i 等於 j) } arr[j] = baseVal // 將基準值放至中央位置完成一次循環(這時候 j 等於 i ) sort(arr, left, j-1) // 將左邊的無序數組重複上面的操作 sort(arr, j+1, right) // 將右邊的無序數組重複上面的操作 } const newArr = array.concat() // 為了保證這個函式是純函式拷貝一次數組 sort(newArr) return newArr}
Java
class Quick { public void sort(int arr[],int low,int high) { int l=low; int h=high; int povit=arr[low]; while(l<h) { while(l<h&&arr[h]>=povit) h--; if(l<h){ arr[l]=arr[h]; l++; } while(l<h&&arr[l]<=povit) l++; if(l<h){ arr[h]=arr[l]; h--; } } arr[l]=povit; print(arr); System.out.print("l="+(l+1)+"h="+(h+1)+"povit="+povit+"\n"); if(l-1>low)sort(arr,low,l-1); if(h+1<high)sort(arr,h+1,high); }}/*//////////////////////////方式二////////////////////////////////*/更高效點的代碼:public<TextendsComparable<?superT>>T[]quickSort(T[]targetArr,intstart,intend){inti=start+1,j=end;Tkey=targetArr[start];SortUtil<T>sUtil=newSortUtil<T>();if(start=end)return(targetArr);/*從i++和j--兩個方向搜尋不滿足條件的值並交換**條件為:i++方向小於key,j--方向大於key*/while(true){while(targetArr[j].compareTo(key)>0)j--;while(targetArr[i].compareTo(key)<0&&i<j)i++;if(i>=j)break;sUtil.swap(targetArr,i,j);if(targetArr[i]==key){j--;}else{i++;}}/*關鍵數據放到‘中間’*/sUtil.swap(targetArr,start,j);if(start<i-1){this.quickSort(targetArr,start,i-1);}if(j+1<end){this.quickSort(targetArr,j+1,end);}returntargetArr;}/*//////////////方式三:減少交換次數,提高效率/////////////////////*/private<TextendsComparable<?superT>>voidquickSort(T[]targetArr,intstart,intend){inti=start,j=end;Tkey=targetArr[start];while(i<j){/*按j--方向遍歷目標數組,直到比key小的值為止*/while(j>i&&targetArr[j].compareTo(key)>=0){j--;}if(i<j){/*targetArr[i]已經保存在key中,可將後面的數填入*/targetArr[i]=targetArr[j];i++;}/*按i++方向遍歷目標數組,直到比key大的值為止*/while(i<j&&targetArr[i].compareTo(key)<=0)/*此處一定要小於等於零,假設數組之內有一億個1,0交替出現的話,而key的值又恰巧是1的話,那么這個小於等於的作用就會使下面的if語句少執行一億次。*/{i++;}if(i<j){/*targetArr[j]已保存在targetArr[i]中,可將前面的值填入*/targetArr[j]=targetArr[i];j--;}}/*此時i==j*/targetArr[i]=key;//應加判斷/*遞歸調用,把key前面的完成排序*/this.quickSort(targetArr,start,i-1);/*遞歸調用,把key後面的完成排序*/this.quickSort(targetArr,j+1,end);//兩個遞歸應加判斷}
C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace test{ class QuickSort { static void Main(string[] args) { int[] array = { 49, 38, 65, 97, 76, 13, 27 }; sort(array, 0, array.Length - 1); Console.ReadLine(); } /**一次排序單元,完成此方法,key左邊都比key小,key右邊都比key大。 **@param array排序數組 **@param low排序起始位置 **@param high排序結束位置 **@return單元排序後的數組 */ private static int sortUnit(int[] array, int low, int high) { int key = array[low]; while (low < high) { /*從後向前搜尋比key小的值*/ while (array[high] >= key && high > low) --high; /*比key小的放左邊*/ array[low] = array[high]; /*從前向後搜尋比key大的值,比key大的放右邊*/ while (array[low] <= key && high > low) ++low; /*比key大的放右邊*/ array[high] = array[low]; } /*左邊都比key小,右邊都比key大。//將key放在游標當前位置。//此時low等於high */ array[low] = key; foreach (int i in array) { Console.Write("{0}\t", i); } Console.WriteLine(); return high; } /**快速排序 *@paramarry *@return */ public static void sort(int[] array, int low, int high) { if (low >= high) return; /*完成一次單元排序*/ int index = sortUnit(array, low, high); /*對左邊單元進行排序*/ sort(array, low, index - 1); /*對右邊單元進行排序*/ sort(array, index + 1, high); } }}
13 27 38 49 65 76 97
F#
let rec qsort = function [] -> [] |x::xs -> qsort [for i in xs do if i < x then yield i]@ x::qsort [for i in xs do if i >= x then yield i]
PHP
<?php/** * 快速排序 * @author chaselxu */function qs($a, $s=0, $e=null) { if($e === null) { $e = count($a); } if($s >= $e) { return $a; } $j = $e-1; $i = $s; $n = $a[$s]; //$ac = $a; for(; $j>$i; $j--) { if($a[$j] < $n) { $t = $a[$i]; $a[$i] = $a[$j]; $a[$j] = $t; for($i++;$i<$j;$i++) { if($a[$i] > $n) { $t = $a[$j]; $a[$j] = $a[$i]; $a[$i] = $t; break; } } } } //printf("\n[%s], %s ~ %s [%s] %s-%s-%s", implode($ac, ','), $s, $e, implode($a, ','), $i, $j, $e); qs(&$a, $s, $i); qs(&$a, $i+1, $e); return $a;}$n = mt_rand(10000000, 99999999);$n = '7,3,2,8,7,2,0,2';//trim(preg_replace('/(\d)/', '$1,', $n), ',');$n = explode(',', $n);echo "\n".implode($n, ',');echo "\n".implode(qs($n), ',');
Pascal
這裡是完全程式,過程部分為快排program qsort;var n,p:integer; a:array[0..100000] of integer;procedure qs(l,r:integer);//假設被排序的數組是a,且快排後按升序排列)var i,j,m,t:integer;begin i:=l; j:=r;//(l(left),r(right)表示快排的左右區間) m:=a[(l+r)div2];//注意:本句不能寫成:m:=(l+r)div2; repeat while a[i]<m do inc(i); while a[j]>m do dec(j);//若是降序把'<'與‘>'互換; if i<=j then begin t:=a[i]; a[i]:=a[j]; a[j]:=t; inc(i); dec(j); end; until i>j; if l<j then qs(l,j);//遞歸查找左區間 if i<r then qs(i,r);//遞歸查找右區間end;begin readln(n);//有n個數據要處理 for p:=1 to n do read(a[p]);//輸入數據 qs(1,n); for p:=1 to n do write(a[p],'');//輸出快排後的數據end.或者procedure quickSort(var a:array of integer;l,r:Integer);var i,j,x:integer;begin if l>=r then exit; i:=l; j:=r; x:=a[i]; while i<=j do begin while (i<j)and(a[j]>x) do dec(j); if i<j then begin a[i]:=a[j]; inc(i); end; while (i<j)and(a[i]<x) do inc(i); if i<j then begin a[j]:=a[i]; dec(j); end; a[i]:=x; quicksort(a,l,i-1); quicksort(a,i+1,r); end;end;
Python3:分而治之+遞歸
def quick_sort(data): """快速排序""" if len(data) >= 2: # 遞歸入口及出口 mid = data[len(data)//2] # 選取基準值,也可以選取第一個或最後一個元素 left, right = [], [] # 定義基準值左右兩側的列表 data.remove(mid) # 從原始數組中移除基準值 for num in data: if num >= mid: right.append(num) else: left.append(num) return quick_sort(left) + [mid] + quick_sort(right) else: return data# 示例:array = [2,3,5,7,1,4,6,15,5,2,7,9,10,15,9,17,12]print(quickSort(array))# 輸出為[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]
最佳化
三平均分區法
(1) 首先,它使得最壞情況發生的幾率減小了。
(2) 其次,未改進的快速排序算法為了防止比較時數組越界,在最後要設定一個哨點。
根據分區大小調整算法
另一種最佳化改進是當分區的規模達到一定小時,便停止快速排序算法。也即快速排序算法的最終產物是一個“幾乎”排序完成的有序數列。數列中有部分元素並沒有排到最終的有序序列的位置上,但是這種元素並不多。可以對這種“幾乎”完成排序的數列使用插入排序算法進行排序以最終完成整個排序過程。因為插入排序對於這種“幾乎”完成的排序數列有著接近線性的複雜度。這一改進被證明比持續使用快速排序算法要有效的多。
另一種快速排序的改進策略是在遞歸排序子分區的時候,總是選擇優先排序那個最小的分區。這個選擇能夠更加有效的利用存儲空間從而從整體上加速算法的執行。
不同的分區方案考慮
對於這種情況的一種改進辦法就是將分區分為三塊而不是原來的兩塊:一塊是小於中軸值的所有元素,一塊是等於中軸值的所有元素,另一塊是大於中軸值的所有元素。另一種簡單的改進方法是,當分區完成後,如果發現最左和最右兩個元素值相等的話就避免遞歸調用而採用其他的排序算法來完成。
並行的快速排序
在大多數情況下,創建一個執行緒所需要的時間要遠遠大於兩個元素比較和交換的時間,因此,快速排序的並行算法不可能為每個分區都創建一個新的執行緒。一般來說,會在實現代碼中設定一個閥值,如果分區的元素數目多於該閥值的話,就創建一個新的執行緒來處理這個分區的排序,否則的話就進行遞歸調用來排序。
對於這一併行快速排序算法也有其改進。該算法的主要問題在於,分區的這一步驟總是要在子序列並行處理之前完成,這就限制了整個算法的並行程度。解決方法就是將分區這一步驟也並行處理。改進後的並行快速排序算法使用2n個指針來並行處理分區這一步驟,從而增加算法的並行程度。