你們好,感謝你們對前兩篇博客的支持。今天我準備提供歸併,快排,快速選擇的筆記,這三個是分治算法的典型例子。此次有利用疊縮證實算法的時間界限哦!,另外代碼已經放到碼雲上啦。java
分治算法的基本思想是將一個規模爲N的問題分解爲K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可獲得原問題的解。即一種分目標完成程序算法,簡單問題可用二分法完成。
歸併,快排,快速選擇是分治思想的三個典型例子。git
通常排序使用Java提供的歸併算法,即Collections.sort(..)。快速選擇通常用於解決TopN問題, 下面分別介紹三個算法。github
歸併排序(MERGE-SORT)是創建在歸併操做上的一種有效的排序算法。算法
該算法的基本操做是合併兩個已排序的表,下面舉例說明合併過程: 數組
算法描述詳細描述:函數
private static <AnyType extends Comparable<? super AnyType>> void mergeSort(AnyType[] a, AnyType[] tmpArray, int left, int right) { if (left < right) { int center = (left + right) / 2; // 以下方式排除了只有3個元素的狀況,只有三個元素是 center=1 mergeSort(a, tmpArray, left, center); mergeSort(a, tmpArray, center + 1, right); merge(a, tmpArray, left, center + 1, right); } } /** * 合併函數,歸併排序的基本步驟 * @param a 原始數據數組 * @param tmpArray 歸併使用的第三個臨時數組 * @param leftPos 左邊部分開始,對應圖上Actr初始位置 * @param rightPos 右邊開始 ,對應Bctr初始位置 * @param rightEnd 右邊結束 ,對應Bctr結束位置 */ private static <AnyType extends Comparable<? super AnyType>> void merge(AnyType[] a, AnyType[] tmpArray, int leftPos, int rightPos, int rightEnd) { // 必定範圍內合併 //左邊結束,對應圖上Actr結束位置 int leftEnd = rightPos - 1; int tmpPos = leftPos; //本次合併總共包含的元素數量 int numElements = rightEnd - leftPos + 1; //進行歸併 while (leftPos <= leftEnd && rightPos <= rightEnd) { if (a[leftPos].compareTo(a[rightPos]) <= 0) { tmpArray[tmpPos++] = a[leftPos++]; } else { tmpArray[tmpPos++] = a[rightPos++]; } } while (leftPos <= leftEnd) { tmpArray[tmpPos++] = a[leftPos++]; } while (rightPos <= rightEnd) { tmpArray[tmpPos++] = a[rightPos++]; } // 將排序過的數據拷貝會原始數組,【只有rightEnd沒有變化】。 for (int i = 0; i < numElements; i++, rightEnd--) { a[rightEnd] = tmpArray[rightEnd]; } }
根據前面的描述能夠得出以下通項公式:
ui
使用疊縮求和,進行推導,兩邊除以Nurl
以後進行求和兩邊減去公項後結果爲:
.net
以後兩邊同乘以N,獲得時間界:
3d
快速排序(Quicksort) 的基本思想是:經過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的全部數據都比另一部分的全部數據都要小,而後再按此方法對這兩部分數據分別進行快速排序,整個排序過程能夠遞歸進行,以此達到整個數據變成有序序列
/** * 三數中值分割法 * @param a 原始數組 * @param left 左邊界 * @param right 右邊界 * @return 返回樞元 */ private static <AnyType extends Comparable<? super AnyType>> AnyType median3(AnyType[] a, int left, int right) { int center=(left+right)/2; if(a[center].compareTo(a[left])<0){ swapReferences(a, left, center); } if(a[right].compareTo(a[left])<0){ swapReferences(a, left, right); } if(a[right].compareTo(a[center])<0){ swapReferences(a, center, right); } swapReferences(a, center, right-1); return a[right-1]; } /** * 快排核心 * @param a 原始數組 * @param left 左邊界 * @param right 右邊界 */ private static <AnyType extends Comparable<? super AnyType>> void quickSort(AnyType[] a, int left, int right) { if(left+CUTOFF<=right){ //三數中值分割法,取樞元 AnyType pivot=median3(a,left,right); int i=left; int j=right-1; while(true){ //i找大於樞元的元素 while(a[++i].compareTo(pivot)<0); //j找小於樞元的元素 while(a[--j].compareTo(pivot)>0); if(i<j){ swapReferences(a, i, j); }else{//i>j,此輪分割結束 break; } } //交換i,與樞元 swapReferences(a, i, right-1); //分治進行,快排 quickSort(a,0,i-1); quickSort(a,i+1,right); }else{ insertSort(a, left, right); } }
假設S1,每一個大小都是等可能的。
因爲該假設,可知
和
的平均值爲
能夠獲得通項爲:
兩邊乘以N獲得式子1以下:
由N通項得出N-1通項以下爲式子2:
式子1-式子2,獲得以下:
除去無關係-c,進行疊縮:
進行求和:
該和大概爲O(logN),因而獲得平均時間界限:
亂序集合中找到第K個最小元。
依照快排思路進行處理:
則將S排序返回第k個最小元。 2. 以三數中值取樞元v 3. 將S-{v}劃分爲兩個不相交集合,並肯定樞元v的位置:
4. 若是
第K個元素在
中,返回
不然返回:
/** * 快速選擇核心 * @param a 原始數組 * @param left 左邊界 * @param right 右邊界 * @param k 須要選擇的位 */ private static<AnyType extends Comparable<? super AnyType>> void quickSelect(AnyType[] a, int left, int right, int k) { if(left+CUTOFF<=right){ AnyType pivot=median3(a, left, right); int i=left; int j=right-1; while(true){ while(a[++i].compareTo(pivot)<0); while(a[--j].compareTo(pivot)>0); if(i<j){ swapReferences(a, i, j); }else{ break; } } swapReferences(a, i, right-1); if(k<=i){ quickSelect(a, left, i-1, k); }else if(k>i+1){ quickSelect(a, i+1, right, k); } }else{ insertSort(a, left, right); } }
碼雲: 歸併&快排: 點擊查看 快速選擇 : 點擊查看
github: 歸併&快排 快速選擇