合并排序(分治)

时间:2022-12-04 11:07:04
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN100

int a[MAXLEN];//用于存放目标序列

void merg(int a[] , int b[] , size_t left , size_t mid , size_t right) {//merg(合并)
//将a中下标从left到mid(middle)和从mid + 1到right的两个子序列进行合并
//前提是该两个子序列都已经排好序
//最后将合并好的序列存入b中的相应位置

size_t i , j , k;

for ( i = left , j = mid + 1 , k = left ;
i <= mid && j <= right ;
k++ ) {//从两个子序列的头部开始逐个比较,将较小的元素逐个存入b中

if ( a[i] < a[j] )
b[k] = a[i++];
else
b[k] = a[j++];
}

//将a或b中多余的元素直接插到b的最后
while ( i <= mid )
b[k++] = a[i++];
while ( j <= right )
b[k++] = a[j++];
}

void mpass(int a[] , int b[] , size_t len , size_t size) {//merge pass
//从左到右按照指定宽度size将a合并,并存入b中

size_t i = 0;

while ( len - i >= size + size ) {

merg(a , b , i , i + size - 1 , i + size + size - 1);
i += size + size;//每次让i移动两倍宽度
}

if ( len - i > size )//如果剩余序列的长度大于一倍size,则将前size部分和后面不足size的部分合并
merg(a , b , i , i + size - 1 , len - 1);
else//如果剩余序列长度不足size,则直接插入到b的最后
while ( i < len )
b[i++] = a[i];
}

void msort(int a[] , int b[] , size_t len) {//merge sort(合并排序)
//借助b的临时空间对a进行合并排序,最后结果是存在a中的!!!

size_t size = 1;

while ( size < len ) {

mpass(a , b , len , size);//先将a倒入b
size += size;
mpass(b , a , len , size);//再将b倒入a,以保证最终结果是存在a中的
size += size;
}//利用b在a、b之间来回倒要比每次在merg中malloc临时空间直接将排好的结果存放在a中快n倍,频繁malloc将导致频繁访问内核(速度是非常慢的!!)
}

int main() {

int *b = malloc( MAXLEN * sizeof(int) );//如果a过大,再用栈分配临时空间可能会导致栈崩,因此使用堆中的空间
//为了避免多再次在merg中malloc,因此直接在main中一次性malloc足够的空间
if ( !b )
exit(EXIT_SUCCESS);

size_t len;
scanf("%u" , &len);
int i;
for ( i = 0 ; i < len ; i++ )
scanf("%d" , a + i);

msort(a , b , len);
free(b);

for ( i = 0 ; i < len ; i++ )
printf("%d " , a[i]);
putchar('\n');

return 0;
}

测试数据(两组,执行两次):

5         input

5 4 3 2 1

1 2 3 4 5         output


7            input

3 45 12 34 5 44 8

3 6 8 12 34 44 45         output