/*
归并排序。 思路很简单,就是不断的将要排序的数组进行分割,然后直到只有一个数的时候,那么这个时候就是已经排好序的了。 然后将这两部分数存放在不同的两个数组中,两边都从最小的部分开始往外读,回归到排序数组中即可。
SQ 2014.4.14
*/
#include<stdio.h>
int aa[10]={10,8,9,7,6,4,5,3,1,2};
void merge(int start , int mid , int end){
int left[10];
int right[10];
int i , j , k;
int n1 ,n2; //用于存放左右两部分的长度
n1 = mid -start +1;// 比如 0-5 吧,其实这里的数据是6个 然后就是 <5-0 +1>;
n2= end -mid;
for(i =0 ; i < n1 ;i++) //将待排序的左半部分存放在数组中
left[i] = aa[start +i];
for(j=0 ; j < n2 ; j++)
right[j] = aa[j + mid +1];
i=j=0;
for ( k= start; i < n1 && j <n2 ;k++ ) { // 从两个数组中按大小取出数列
if (left[i] <= right[j]){ //如果左边的小于右边的,则取左边的 然后左边的+1;
aa[k] = left[i];
i++;
}
else if(left[i] > right[j]) {
aa[k] = right[j];
j++;
}
}
for (; i < n1; i ++) {
aa[k] = left[i];
k++;
}
for(; j < n2 ; j++){
aa[i+j] = right[j];
k++;
}
}
//开始写分割函数
void sort(int start ,int end ){
int mid ;
if (start < end) {
mid = (start + end )/2 ;
sort (start ,mid ); //这里是偶数的话倒没有问题,奇数的话左半部分的比右半部分的多一;
sort (mid +1 ,end);
merge( start ,mid ,end);
}
}
int main(){
int i;
for(i=0 ; i < 10 ;i++)
printf("%d \n" ,aa[i]);
sort(0 , 9);
printf("after sort()\n");
for(i=0 ; i < 10 ;i++)
printf( "%d \n " ,aa[i]);
return 0;
}
分析时间复杂度:
这是一个递推公式(Recurrence),我们需要消去等号右侧的T(n),把T(n)写成n的函数。其实符合一定条件的Recurrence的展开有数学公式可以套,这里我们略去严格的数学证明,只是从直观上看一下这个递推公式的结果。当n=1时可以设T(1)=c1,当n>1时可以设T(n)=2T(n/2)+c2n,我们取c1和c2中较大的一个设为c,把原来的公式改为:
这样计算出的结果应该是T(n)的上界。下面我们把T(n/2)展开成2T(n/4)+cn/2(下图中的(c)),然后再把T(n/4)进一步展开,直到最后全部变成T(1)=c(下图中的(d)):
把图(d)中所有的项加起来就是总的执行时间。这是一个树状结构,每一层的和都是cn,共有lgn+1层,因此总的执行时间是cnlgn+cn,相比nlgn来说,cn项可以忽略,因此T(n)的上界是Θ(nlgn)。
如果先前取c1和c2中较小的一个设为c,计算出的结果应该是T(n)的下界,然而推导过程一样,结果也是Θ(nlgn)。既然T(n)的上下界都是Θ(nlgn),显然T(n)就是Θ(nlgn)。
空间复杂度的话显然是O(n).因为这里只需要一个数组来存放左右两部分数据。