快速排序算法

[size=small]排序是數據處理領域一種最常用的運算,排序的目的主要是爲了快速查找。
常用的算法有:選擇排序、快速排序、希爾排序、堆排序、冒泡排序、插入排序、歸併排序。
其中選擇排序、快速排序、希爾排序、堆排序不是穩定的排序算法,而冒泡排序、插入排序、歸併排序和基數排序是穩定的排序算法。
其中這些排序算法中,以快速排序和二路歸併排序效率較高,並且在面試時稍微深入算法面試官都會問到的。本次博文講解快速排序。

[b]快速排序[/b]
快速排序由於排序效率在同爲O(N*logN)的幾種排序方法中效率較高,因此經常被採用,再加上快速排序思想----分治法也確實實用,因此很多軟件公司的筆試面試,包括像騰訊,微軟等知名IT公司都喜歡考這個,還有大大小的程序方面的考試如軟考,考研中也常常出現快速排序的身影。
總的說來,要直接默寫出快速排序還是有一定難度的,因爲本人就自己的理解對快速排序作了下白話解釋,希望對大家理解有幫助,達到快速排序,快速搞定。



快速排序是C.R.A.Hoare於1962年提出的一種劃分交換排序。它採用了一種分治的策略,通常稱其爲分治法(Divide-and-ConquerMethod)。

該方法的基本思想是:

1.先從數列中取出一個數作爲基準數。

2.分區過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。

3.再對左右區間重複第二步,直到各區間只有一個數。



雖然快速排序稱爲分治法,但分治法這三個字顯然無法很好的概括快速排序的全部步驟。因此我的對快速排序作了進一步的說明:挖坑填數+分治法:

舉例來說,現在有一個數據集{85, 24, 63, 45, 17, 31, 96, 50},怎麼對其排序呢?
第一步,選擇中間的元素45作爲"基準"。(基準值可以任意選擇,但是選擇中間的值比較容易理解。)
[img]http://dl2.iteye.com/upload/attachment/0119/4302/c6bdc989-b3e3-35e4-945a-b258a1f22baf.png[/img]
第二步,按照順序,將每個元素與"基準"進行比較,形成兩個子集,一個"小於45",另一個"大於等於45"。
[img]http://dl2.iteye.com/upload/attachment/0119/4304/0ef995ee-ae9c-3b0a-8ab7-2a2dd7675c1c.png[/img]
第三步,對兩個子集不斷重複第一步和第二步,直到所有子集只剩下一個元素爲止。
[img]http://dl2.iteye.com/upload/attachment/0119/4306/8131e3fa-587a-37f0-bdf7-a0619643195e.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0119/4308/bd50dc8f-f843-366c-adb9-94a688005b8d.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0119/4310/29248705-886c-31a0-9a9d-c62206843a52.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0119/4312/ac3da9b3-04ac-3d51-b891-70beaabe7d3c.png[/img]

參考網上和書上的資料,得出如下代碼:
[b]注意:我這裏代碼中爲了簡單起見,讓第一個元素值爲基準值。[/b]
代碼方法:

/**
* 快速排序算法,完成一次區間劃分,返回中間位置j
* 時間複雜度O(Nlog₂N)其中N爲數組長度
* @param a
* @param s
* @param t
* @return
*/
public static int partition(Object[] a, int s, int t) {
// 1、給i,j賦初值
int i = s;
int j = t;
// 2、將基準元素的值賦給臨時變量,並讓i++
Object x = a[i++];
// 3、i<=j時,依次檢查需要進行交換位置的元素
while(i <= j) {
// 從前向後進行順序查找一個大於基準值的元素,該元素需要與後面的區間中的某一個元素交換位置
while(i <= j && ((Comparable)a[i]).compareTo(x) <= 0) {
i++;
}
// 從後向前進行順序查找一個小於基準值的元素,該元素需要與前面的區間中的某一個元素交換位置
while(j >= i && ((Comparable)a[j]).compareTo(x) >= 0) {
j--;
}
// 當條件成立時,交換a[i],a[j]的位置
if (i < j) {
Object temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
}
}
// 交換a[s]與a[j]的值
a[s] = a[j];
a[j] = x;
return j;
}

/**
* 遞歸調用快速排序算法進行排序,本質上是分治的思想
* @param a
* @param s
* @param t
*/
public static void quickRecursion(Object[] a, int s, int t) {
// 進行第一次快速排序
int j = partition(a, s, t);
// 對左區間進行快速排序
if(s < j - 1) {
quickRecursion(a, s, j-1);
}
// 對右區間進行快速排序
if(j + 1 < t) {
quickRecursion(a, j+1, t);
}
}

/**
* 主函數測試
* @param args
*/
public static void main(String[] args) {
Object[] a1 = {8,9,4,3,5,7,2,1};
System.out.println("初始數組:" + Arrays.toString(a1));
quickRecursion(a1, 0, a1.length-1);
System.out.println(Arrays.toString(a1));
}


[b]運行結果:[/b]
[i]初始數組:[8, 9, 4, 3, 5, 7, 2, 1]
經過排序後:[1, 2, 3, 4, 5, 7, 8, 9][/i]

在快速排序中,若把基準元素看做根結點,若把劃分得到的左區間和右區間看做根結點的左子樹和右子樹,那麼整個排序過程就對應着一棵具有n個元素的二叉搜索樹。在快速排序中,記錄的移動次數通常小於記錄的比較次數。時間複雜度爲[O(N*logN),O(n²)],空間複雜度爲[O(log₂N),O(n)]。
大量的實踐證明:當n較大時,它是目前爲止平均情況下速度最快的一種排序算法。平均情況下,快速排序的比較次數爲理想情況的2ln2≈1.39倍,所以平均的時間複雜度仍然爲
O(log₂N)。

[b]注意:快速排序不適用與基本有序的無序表。
當然,爲了避免這種情況發生,也可以修改快速排序算法,每次劃分前比較當前區間的第一個元素,中間元素和最後一個元素的值,取中間值爲基準元素,這樣就可以避免快速排序淪爲簡單排序了[/b]

參考:
《數據結構使用教程Java語言描述》徐孝凱
[url]http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html[/url]
[url]http://blog.csdn.net/morewindows/article/details/6684558[/url]

[/size]
發佈了151 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章