經典算法4:分治法求解快速排序
簡介:
歸 并排序將整個集合問題分解為最小單元,將該單元n1內的內容全部排序,然后將相鄰的單元n1,n2重新排序。如果將n1,n2看做一個整體n的話,則針對 n,先對其一半進行排序,另一半排序,然后整體再次排序。符合我們一般的做事習慣,將大問題都分解為小問題,針對小問題逐一解決,最終解決掉整個問題,最 先解決的是小問題,最后大問題迎刃而解。
而 快速排序似乎反其道而行之,從一開始就將整個單元n進行粗略分類,左側是<a[i],右側是>=a[i],然后再針對左側和右側進行類似處理。這個 類似于自上而下的命令傳達,最上層的僅僅初次完成分類目標,具體的子任務留給下一層節點處理。先處理的是整體,最終小問題解決后,問題終止。
/// <summary>
/// 快速排序
/// 以Int數據類型為例
/// </summary>
public class FengQuickSort
{
/// <summary>
/// 快速排序
/// 索引范圍[startIndex,endIndex]
/// </summary>
/// <param name="arr">待排序數組</param>
/// <param name="startIndex">起始索引</param>
/// <param name="endIndex">終止索引</param>
public void QuickSort(int[] arr, int startIndex, int endIndex)
{
if (startIndex < endIndex)
{
int midIndex = PartitionAjust(arr, startIndex, endIndex);
QuickSort(arr, startIndex, midIndex-1);
QuickSort(arr, midIndex + 1, endIndex);
}
}
//調整范圍為[leftIndex,rightIndex]
//返回調整后的中軸索引值
private int PartitionAjust(int[] arr, int leftIndex, int rightIndex)
{
//比較的中軸值
int midValue = arr[leftIndex];
int leftFlag = leftIndex;
int rightFlag = rightIndex;
while (leftFlag < rightFlag)
{
//從右向左找到小于中軸值的值
while (leftFlag < rightFlag && arr[rightFlag] >= midValue)
{
--rightFlag;
}
arr[leftFlag] = arr[rightFlag];
//從左向右找到大于中軸值的值
while (leftFlag < rightFlag && arr[leftFlag] <= midValue)
{
++leftFlag;
}
arr[rightFlag] = arr[leftFlag];
}
arr[leftFlag] = midValue;
return leftFlag;
}
}
該方法的基本思想是:
1.先從數列中取出一個數作為基準數。
2.分區過程,將比這個數大的數全放到它的右邊,小于或等于它的數全放到它的左邊。
3.再對左右區間重復第二步,直到各區間只有一個數。
雖然快速排序稱為分治法,但分治法這三個字顯然無法很好的概括快速排序的全部步驟。因此我的對快速排序作了進一步的說明:挖坑填數+分治法:
先來看實例吧,定義下面再給出(最好能用自己的話來總結定義,這樣對實現代碼會有幫助)。
以一個數組作為示例,取區間第一個數為基準數。
初始時,i = 0; j = 9; X = a[i] = 72
由于已經將a[0]中的數保存到X中,可以理解成在數組a[0]上挖了個坑,可以將其它數據填充到這來。
從j開始向前找一個比X小或等于X的數。當j=8,符合條件,將a[8]挖出再填到上一個坑a[0]中。a[0]=a[8]; i++; 這樣一個坑a[0]就被搞定了,但又形成了一個新坑a[8],這怎么辦了?簡單,再找數字來填a[8]這個坑。這次從i開始向后找一個大于X的數,當 i=3,符合條件,將a[3]挖出再填到上一個坑中a[8]=a[3]; j--;
數組變為:
i = 3; j = 7; X=72
再重復上面的步驟,先從后向前找,再從前向后找。
從j開始向前找,當j=5,符合條件,將a[5]挖出填到上一個坑中,a[3] = a[5]; i++;
從i開始向后找,當i=5時,由于i==j退出。
此時,i = j = 5,而a[5]剛好又是上次挖的坑,因此將X填入a[5]。
數組變為:
可以看出a[5]前面的數字都小于它,a[5]后面的數字都大于它。因此再對a[0…4]和a[6…9]這二個子區間重復上述步驟就可以了。
對挖坑填數進行總結
1.i =L; j = R; 將基準數挖出形成第一個坑a[i]。
2.j--由后向前找比它小的數,找到后挖出此數填前一個坑a[i]中。
3.i++由前向后找比它大的數,找到后也挖出此數填到前一個坑a[j]中。
4.再重復執行2,3二步,直到i==j,將基準數填入a[i]中。