《Java数据结构和算法》如此描述分治算法:
把一个大问题分成两个相对来说更小的问题,并且分别解决每一个小问题,对每一个小问题的解决方案是一样的:把每个小问题分成两个更小的问题,并且解决它们。这个过程一直持续小去知道达到易于求解的基值情况,就不用再继续分了。
时间复杂度:O(N*log2N)
首先我们是先归并两个有序数组,
归并流程如下:
代码如下:
/**
* 归并算法
* @author Administrator
*
*/
public class MergeApp {
public static void main(String[] args) {
int[] arrayA = {22,32,45,79};
int[] arrayB = {7,14,28,35,88,93};
int[] arrayC = new int[10];
merge(arrayA,4,arrayB,6,arrayC);
display(arrayC,10);
}
public static void merge(int[] arrayA,int sizeA,int[] arrayB,int sizeB,int[] arrayC) {
int aDex = 0,bDex = 0,cDex = 0;
while(aDex<sizeA && bDex<sizeB) {
if(arrayA[aDex]<arrayB[bDex])
arrayC[cDex++] = arrayA[aDex++];
else
arrayC[cDex++] = arrayB[bDex++];
}
while(aDex<sizeA)
arrayC[cDex++] = arrayA[aDex++];
while(bDex<sizeB)
arrayC[cDex++] = arrayB[bDex++];
}
public static void display(int[] arrayC,int sizeC) {
for(int i=0;i<sizeC;i++) {
System.out.print(arrayC[i]+" ");
}
System.out.println();
}
}
进入正题:
我们将数组拆成两个,两个又分别拆成两个成了四个,四个又分别拆成两个,一共成了八个...这样以此类推
(以左半部分)
和
继续拆:
和
这已经拆到lowerBound==upperBound的时候了,这时候我们就合并红色和绿色的,又按照这个过程从下往上顺回去
怎么合并呢?
我们前面已经提过了如何将两个有序数组合并到一个数组里面了对不对?我们刚刚把一个好好的乱序数组一直拆分一直拆分下去,一直到只有一个数据元素的时候不再进行拆分,我们想当然的也把它们称之为数组(方便跟前面的如何将两个有序数组合并起来),想想看只有一个数据元素的数组,是不是相当于是个有序数组呢?因为它只有一个数据元素啊,不用排序说明它已经排序好了,然后我们将两个已经排序好的数组(只有一个数据元素的数组)按照前面“将两个有序数组进行合并”的方法,放到名为workSpace数组(一个空数组)里面,合并完成以后不就有了一个拥有两个数据元素的有序数组了吗?然后我们又跟其它的数组比较,比如这里的红红和绿绿合并之后,再跟黄黄合并接着放到workSpace里面,然后一直往上推,我们来到将一个数组一分为二的合并,是不是感到很熟悉,是的,这样的合并我们之前讲到了。瞧,是不是每一步都是这样的,本质就是两有序合并,两有序数组合并...
理解拆分数组如图所示:(本质还是同一个数组)【理解用于merge()方法中】
我们将这个数组拆成两半来看,一半是由6 1 8构成,另一半由10 9 7构成,但他们本质上还是在一个数组里面的,并没有达到真正的拆,我们在抽象的拆,其运用的操作就是得到不同的小标,如6 1 8 我们如何得到它,只需得到其下标范围0~2即可,同理得到下标3~5,这样我们就相当于将一个数组拆成了两个数组。
友情提示:结合代码和注释更容易理解哦!
public class NewDarray {
private int[] theArray;
private int nElems;
public NewDarray(int maxSize) {
theArray = new int[maxSize];
nElems = 0;
}
//往数组插入元素
public void insert(int num) {
theArray[nElems++] = num;
}
//打印数组元素
public void display() {
for(int i=0;i<nElems;i++)
System.out.print(theArray[i]+" ");
System.out.println();
}
//调用排序
public void mergeSort() {
int[] workSpace = new int[nElems];
reMergeSort(workSpace,0,nElems-1);
}
public void reMergeSort(int[] workSpace,int lowerBound,int upperBound){
//获取中间下标
int mid = (lowerBound+upperBound)/2;
//当lowerBound域upperBound相等的时候表示其只有一个数据元素并不需要排列,返回
if(lowerBound==upperBound)
return;
//排左边一半
reMergeSort(workSpace,lowerBound,mid);
//排右边
reMergeSort(workSpace,mid+1,upperBound);
//将两个有序数组进行合并
merge(workSpace,lowerBound,mid+1,upperBound);
}
public void merge(int[] workSpace,int lowerPtr,int higherPtr,int upperBound) {
//我们是将元素排序好以后再复制到theArray(原来的数组身上),所以我们需要获取其元素个数n
//我们还需要复制的起始位置即lowerBound
//还需要获取中间下标,使左半部分得到排序
int j=0;
int lowerBound = lowerPtr;//获取起始下标
int n = upperBound-lowerBound+1;//获取元素个数
int mid = higherPtr-1;//获取中间元素
//在前面已经有如何将两个有序数组合并,这是同一个道理
//我们是将theArray拆分成两个数组来进行合并的,两个分别又可以拆为两个成四个,四个又可以拆...
//合并都是同一个道理
//两个"数组"(就是拆分过的数组,其本质上还是用一个数组)元素
while(lowerPtr<=mid && higherPtr<=upperBound)
if(theArray[lowerPtr]<=theArray[higherPtr])
workSpace[j++] = theArray[lowerPtr++];
else
workSpace[j++] = theArray[higherPtr++];
//将剩下的放进workSpace数组中
while(lowerPtr<=mid)
workSpace[j++] = theArray[lowerPtr++];
while(higherPtr<=upperBound)
workSpace[j++] = theArray[higherPtr++];
//将其复制到数组theArrat中,完成归并
for(j=0;j<n;j++)
theArray[lowerBound+j] = workSpace[j];
}
}