归并排序

我在好几个星期之前就稍微了解了归并排序,但是一直不是很明白,就是处于一种觉得算法很好理解但是不会自己去实现它的状态,现在我终于能自己写出归并排序了,记录一下自己的理解.

首先,要通过递归把一个数组分成只有一个元素的子数组,这样就可以认为这一个数组是有序的,然后再与相邻的数组进行合并排序.

要实现这样的分割其实很简单,只要起始下标和终止下标的值不相等,就应该继续分割.

分割完成之后,要实现相邻两个数组的整体排序,只需要明白如何合并两个有序数组.

假设我们有两个数组,分别是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上的题固定了函数原型,所以只能采取这样的办法了…)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值