分治演演算法-歸併,快排,快速選擇
阿新 • • 發佈:2019-12-31
前言
大家好,感謝大家對前兩篇部落格的支援。今天我準備提供歸併,快排,快速選擇的筆記,這三個是分治演演算法的典型例子。這次有利用疊縮證明演演算法的時間界限哦!,另外程式碼已經放到碼雲上啦。
分治演演算法
分治演演算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程式演演算法,簡單問題可用二分法完成。
歸併,快排,快速選擇是分治思想的三個典型例子。
Java中對應的演演算法
一般排序使用Java提供的歸併演演算法,即Collections.sort(..)。快速選擇一般用於解決TopN問題, 下面分別介紹三個演演算法。
1歸併排序
簡介
歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序演演算法。
演演算法介紹
該演演算法的基本操作是合併兩個已排序的表,下面舉例說明合併過程:
演演算法描述詳細描述:
- 如果N=1,只有一個元素,直接返回
- 否則,前半部分和後半部分各自進行歸併排序,得到排序過的兩部分,之後按照上面的演演算法進行合併。
核心程式碼示例
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,center + 1,right);
merge(a,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,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];
}
}
複製程式碼
分析
根據前面的描述可以得出如下通項公式:
使用疊縮求和,進行推導,兩邊除以N
之後進行求和兩邊減去公項後結果為:
之後兩邊同乘以N,得到時間界:
2快速排序
簡介
快速排序(Quicksort) 的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列
演演算法介紹
- 簡單4步
- 如果S中元素個數為0或1,則返回,或者S中元素小於CUTOFF,則將S排序
- 以三數中值取樞元v
- 將S-{v}劃分為兩個不相交集合,並確定樞元v的位置:
- 按如下順序分治其餘部分處理
- 舉例說明
-
- 三數中值取樞元
即左端,右端和中心的中值作為樞元,(實際能夠減少14%的比較次數). 將最小值放到左端, 最大值放到右端, 中值放到次右端, 原中值處放次右端值。
- 三數中值取樞元
-
- 分割示意
- 分割示意
核心程式碼示意
/**
* 三數中值分割法
* @param a 原始陣列
* @param left 左邊界
* @param right 右邊界
* @return 返回樞元
*/
private static <AnyType extends Comparable<? super AnyType>>
AnyType median3(AnyType[] a,int right) {
int center=(left+right)/2;
if(a[center].compareTo(a[left])<0){
swapReferences(a,center);
}
if(a[right].compareTo(a[left])<0){
swapReferences(a,right);
}
if(a[right].compareTo(a[center])<0){
swapReferences(a,center,right);
}
swapReferences(a,right-1);
return a[right-1];
}
/**
* 快排核心
* @param a 原始陣列
* @param left 左邊界
* @param right 右邊界
*/
private static <AnyType extends Comparable<? super AnyType>>
void quickSort(AnyType[] a,int right) {
if(left+CUTOFF<=right){
//三數中值分割法,取樞元
AnyType pivot=median3(a,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,right-1);
//分治進行,快排
quickSort(a,i-1);
quickSort(a,i+1,right);
}else{
insertSort(a,right);
}
}
複製程式碼
時間複雜度
- 最壞情況
- 平均情況和最好情況
- 平均情況分析
假設S1,每個大小都是等可能的。
由於該假設,可知
和
的平均值為
可以得到通項為:
兩邊乘以N得到式子1如下:
由N通項得出N-1通項如下為式子2:
式子1-式子2,得到如下:
除去無關係-c,進行疊縮:
進行求和:
該和大概為O(logN),於是得到平均時間界限:
3快速選擇
問題描述
亂序集合中找到第K個最小元。
演演算法介紹
依照快排思路進行處理:
- 如果S=1,K=1將S中元素直接返回,若
則將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 right,int k) {
if(left+CUTOFF<=right){
AnyType pivot=median3(a,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,j);
}else{
break;
}
}
swapReferences(a,right-1);
if(k<=i){
quickSelect(a,i-1,k);
}else if(k>i+1){
quickSelect(a,right,k);
}
}else{
insertSort(a,right);
}
}
複製程式碼
總結
- 快排和歸併排序演演算法的平均時間界限是NlogN,Java預設使用歸併演演算法。
- 快速選擇是TopN演演算法的經典解法,但是本人工程中用到較少
完整程式碼地址:
碼雲:
歸併&快排: 點選檢視
快速選擇 : 點選檢視
github:
歸併&快排
快速選擇