經典算法4:分治法求解快速排序

cwf8 11年前發布 | 3K 次閱讀 C/C++ 算法

簡介:

歸 并排序將整個集合問題分解為最小單元,將該單元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.再對左右區間重復第二步,直到各區間只有一個數。

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

先來看實例吧,定義下面再給出(最好能用自己的話來總結定義,這樣對實現代碼會有幫助)。

以一個數組作為示例,取區間第一個數為基準數。

image

 

初始時,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--;

數組變為:

image

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]。

數組變為:

image

可以看出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]中。


 本文由用戶 cwf8 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!