排序演算法之快速排序

2022-09-14 06:57:10 字數 3153 閱讀 3196

1)基本思想

快速排序的基本思想是在陣列中選擇乙個樞紐元素,然後分別從陣列的開始和結束位置處開始遍歷陣列,將比樞紐元素小的都移到樞紐元素的左邊,將比樞紐元素大的元素都移到它的右邊,然後遞迴對樞紐元素前面的元素和後面的元素進行排序。即:

這裡的乙個關鍵地方是如何選取樞紐元,有三種選擇:

a. 選取陣列的第乙個元素 這種策略就是比較懶惰的將得到的陣列的第乙個元素作為樞紐元,如果陣列元素是隨機排列的,這是可行的。但如果陣列是預排序或反序的,那麼這將造成陣列幾乎所有的元素都落在s1或者s2中,這樣一來,最終的時間複雜度將達到o(n^2)。

b. 利用隨機數生成器產生隨機樞紐元 一般來講除非運氣比較差會導致隨機產生的樞紐元導致a中的結果,否則還是比較安全的。但是隨機數生成器的使用成本還是很高的,它會抵消節省下來的平均執行時間。

c.  三數中值法 這種方式分別取出陣列元素的第乙個元素,中間的元素和最後乙個元素進行排序並將中值作為樞紐元,最終的結果是三者中最小的元素放在了陣列的起始位置,最大的元素放到了陣列的最後的位置,三者中的中值放在了中間位置上。

這裡選擇c中的方法來選取樞紐元,並將樞紐元移至陣列的倒數第二個位置,從陣列的第二個位置和陣列的倒數第三個位置開始遍歷陣列,設為位置i和j,當i處的元素大於等於樞紐元時停止,當j位置的元素小於等於樞紐元時停止,如果i < j, 交換兩位置處的元素,然後i和j繼續相向遍歷,直到兩個位置指向同一位置或交錯時,此時,交換i處的元素和樞紐元。這樣的結果就是樞紐元前面的元素都比它小或者等於它,樞紐元後面的元素都比它大或者相等。

採用c中方案的乙個好處就是i和j不會有越界風險,因為陣列的第乙個元素比樞紐元小,所以j不會越界,又因為倒數第二個位置是樞紐元,i停在大於等於樞紐元的位置,所以i也不會越界。

需要注意的地方是,在遍歷到等於樞紐元的位置時,不管i或者j這裡都採用停止而非繼續向前的策略,這裡簡單的做乙個反例:對於乙個全部相同的陣列來說,如果i和j在a[i] = a[j]時不交換繼續向前,那麼必須有乙個方法防止i或者j越界,並且最終樞紐元還是被交換到i最後到過的位置,即倒數第二個位置,最終導致兩個極不均衡的陣列,時間複雜度為o(n^2)。而如果在相等處採取交換的策略,那麼最終得到的是幾乎均等的陣列,排序的複雜度為o(nlogn),i和j會交錯,所以不用擔心越界風險。

2)演算法實現 

1 #include "

stdafx.h

"2 #include 3

using

namespace

std;45

const

int cutoff = 3;6

7void swap(int* a, int*b)813

14int media3(int a, int left, int

right)

1521

if(a[left] >a[right])

2225

if(a[center] >a[right])

2629 swap(&a[center], &a[right-1

]);30

31return a[right-1

];32}33

34void insertsort(int a, int left, int

right)

3546

else

47break;48

}49 a[j] =temp;50}

51}5253

void qsort(int a, int left, int

right)

5463

while(a[--j] >pivot)

64if(i

6568

else

6972

}73 swap(&a[i], &a[right-1

]);74 qsort(a, left, i - 1

);75 qsort(a, i + 1

, right);76}

77else

7881}82

83void print(int a, int

n)84

89 cout<

9192

void quicksort(int a, int

n)93

9798

int _tmain(int argc, _tchar*ar**)99;

101 print(a, 10

);102 quicksort(a, 10

);103

104 system("

pause");

105106

return0;

107 }

view code

3)時間複雜度分析

對於n=5~20,採取插入排序更快,因為不需要遞迴的成本。

最壞情況下,樞紐元是最小元素,那麼:

t(n) = t(n-1) + cn

t(n-1) = t(n-2) + c(n-1)

t(2) = t(1) + c*1

=>  t(n) = t(1) + c(1+2+...+n) = o(n^2)

最好情況下,樞紐元剛好是陣列的中值,那麼陣列剛好被分為兩個均等的陣列:

t(n) = 2t(n/2) + c*n

t(n)/n = t(n/2)/n/2  + c

t(2)/2 = t(1)/1 + c

t(n)/n = t(1)/1 + c*logn

所以,t(n) = n + c*n*logn = o(nlogn).

平均情況下,假設元素大小是等可能的,那麼t(i) = (1/n)(t(1) + t(2) + ... + t(n-1)),那麼:

t(n) = t(i) + t(n-i-1) + c*n = (2/n)(t(0) + t(1) + t(2) + ... + t(n-1)) + c*n.

令 sumn = t(1) + t(2) + ... + t(n-1),則:

nt(n) = 2(t(0) + t(1) + t(2) + ... + t(n-1))+ c*(n^2)

(n-1)t(n-1) = 2(t(0) + t(1) + t(2) + ... + t(n-2)) + c*((n-1)^2)

所以,nt(n) - (n-1)t(n-1) = 2*t(n-1) + 2cn - c

t(n) = o(nlogn). 

排序演算法之快速排序

快速排序使用分治法 divide and conquer 策略來把乙個序列 list 分為兩個子串行 sub lists 步驟為 從數列中挑出乙個元素,稱為 基準 pivot 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面 相同的數可以到任一邊 在這個分割槽退出...

排序演算法之快速排序

快速排序入口 public void quicksort int lists 遞迴呼叫該函式。原理 每次從陣列從選乙個標兵 本實現為簡單起見直接選取給定範圍內的第乙個元素為標兵 讓後在給定範圍內進行雙向遍歷,目的是以標兵為分界線,將所有小於標兵值的數字排一邊,將所有大於標兵的數字 放到另一邊。標兵移...

排序演算法之快速排序

快速排序是一種不穩定的排序演算法,它的基本思想是,以某個元素為基準,將所有大於等於它的值放在右邊,小於它的值放在左邊,這樣陣列就被分為兩部分,遞迴對這兩部分進行快速排序,而單個元素我們認為是已經排好序的。這是一種歸併思想,當然在最後一步,合併,我們什麼也沒有做也不用做。每一次排序都有乙個元素被放在正...