我在好几个星期之前就稍微了解了归并排序,但是一直不是很明白,就是处于一种觉得算法很好理解但是不会自己去实现它的状态,现在我终于能自己写出归并排序了,记录一下自己的理解.
首先,要通过递归把一个数组分成只有一个元素的子数组,这样就可以认为这一个数组是有序的,然后再与相邻的数组进行合并排序.
要实现这样的分割其实很简单,只要起始下标和终止下标的值不相等,就应该继续分割.
分割完成之后,要实现相邻两个数组的整体排序,只需要明白如何合并两个有序数组.
假设我们有两个数组,分别是num1 [10]和num2 [20],并且它们各自有序。
在两个数组中的元素都没有遍历完时,将num1中的第 i +1个与num2中的第 j+1 个进行比较,这里就暗含了 num3 [30] 中的元素已经包含了num1中的 i 个元素与num2中的 j 个元素,也就是这一次比较后的结果要储存到num3的第 i+j+1 个元素里。
又因为这两个数组不一定是等长的,并且就算是等长也不一定会同时遍历完,所以有可能出现一个数组中的数都已经都放到num3中了,但是另一个数组还有未遍历完的元素。这个时候把还未遍历完的元素依次放入结果数组中就好了。
代码如下:
#include<stdlib.h>
typedef struct arr{
int num[10];
int len;
}Arr;
void get_nums(Arr* Arr1,Arr* Arr2);
Arr* sort_nums(const Arr Arr1,const Arr Arr2);
void print_Arr3(const Arr*pArr3);
int main() {
Arr Arr1,Arr2;
Arr* pArr3;
scanf("%d%d",&Arr1.len,&Arr2.len);
get_nums(&Arr1,&Arr2);
pArr3=sort_nums(Arr1,Arr2);
print_Arr3(pArr3);
return 0;
}
void get_nums(Arr* Arr1,Arr* Arr2) {
int i;
for(i=0;i<Arr1->len;i++) {
scanf("%d",&Arr1->num[i]);
}
for(i=0;i<Arr2->len;i++) {
scanf("%d",&Arr2->num[i]);
}
return;
}
Arr* sort_nums(const Arr Arr1,const Arr Arr2) {
Arr* Arr3;
int length=Arr1.len+Arr2.len;
int i=0,j=0;
Arr3=(Arr*)malloc(sizeof(Arr));
Arr3->len=length;
while(i<Arr1.len&&j<Arr2.len) {
if(Arr1.num[i]<Arr2.num[j]) {
Arr3->num[i+j]=Arr1.num[i];
i++;
}
else {
Arr3->num[i+j]=Arr2.num[j];
j++;
}
} //此时num1和num2均有未遍历完的元素
if(i<Arr1.len) {
Arr3->num[i+j]=Arr1.num[i]; //此时num1未遍历完
i++;
}
else if(j<Arr2.len) {
Arr3->num[i+j]=Arr2.num[j]; //此时num2未遍历完
j++;
}
return Arr3;
}
void print_Arr3(const Arr*pArr3) {
int len=pArr3->len;
int i=0;
while(len--) {
printf("%d ",pArr3->num[i]);
i++;
}
printf("\n");
}
(因为刚刚学过struct,所以想用一下,而且一开始写这个的时候还没打算写归并排序~)
明白了这个,再写归并排序就很简单了,首先这是归并排序的函数:
void merge_sort(int*numbers,int left,int right) {
int middle=0;
if(left<right) {
middle=(left+right)/2;
merge_sort(numbers,left,middle);
merge_sort(numbers,middle+1,right);
get_united(numbers,left,middle,right);
}
}
在写这一层函数的时候,不需要考虑到底如何将两个有序数组合并在一起,这是下一层函数需要解决的问题.
void get_united(int*numbers,int left,int middle,int right) {
int i=left,j=middle+1,k=0;
int result[1000]={0};
while(i<=middle&&j<=right) {
if(numbers[i]<numbers[j]) {
result[k]=numbers[i];
i++;
k++;
}
else {
result[k]=numbers[j];
j++;
k++;
}
}
while(i==middle+1&&j<=right) {
result[k]=numbers[j];
j++;
k++;
}
while(j==right+1&&i<=middle) {
result[k]=numbers[i];
i++;
k++;
}
change_numbers(numbers,result,left,right);
}
最后的change_numbers我就不写了,就是把result中的元素复制到原数组中.
(当然,如果在函数参数中加一个临时数组,就不需要一遍遍的每一次合并时都去修改numbers,只需要最后排序完成时整体复制即可)
(但是matrix上的题固定了函数原型,所以只能采取这样的办法了…)