写在前面的话——
这次终于到分治法了,个人觉得分治法比较广泛的认知,就是快速排序法,不过似乎快速排序法只是用到了分治法的思想,但是并没有用到分治法,也不是很明白,反正这次出的题的话,能比较清楚地理解分治法是怎么样一种算法
第四篇——
话说我本来觉得第一题非常麻烦,虽然是用的课本的例题,但是发现这样写的话,有一定几率会出问题,也简单分析了一下,觉得还是快速排序法好用,所以写了两段代码,运行结果的图片懒得传了,但是前几天睡觉的时候,突然想起来这个题,仔细想了想,其实也不是无解的,我的第一段代码改改应该还是能够解出来的,为什么当时没有想明白。简单说明一下,之前发现的错误是,找出来的两个最大或者最小的数,可能是一样的,原因是分组的时候,一个数分成了一组,而这个数如果就是最大或者最小的时候,按照本身的这个代码,判断是有问题的,然后现在想想,因为最终是4个数作比较,最极端的情况是,这4个数,左边两个最小相等,右边两个最小的相等,那么这个情况应该是可以筛选出来的,所以提前判断,反正最小的两个数一定在这4个数中,,如果是要找最小的三个数,那就比较尴尬了!其实也行,4个就应该这个代码就无能为力了,我决定一会儿试试,结果不通知了,虽然这次作业已经过了3个星期了,但是我哦还是决定改改重新提交!
1、利用分治法求一组数据中最大的两个数和最小的两个数
算法设计:
由于在每个子集中都选取最小的两个和最大的两个,回溯结束之后,就可得到一组数据中最小的两个和最大的两个数,从而得到原问题的解。
代码如下
#include <stdio.h>
#define N 10
int num[N];
void maxmin(int left,int right,int * max1,int * max2,int * min1,int * min2)
{
int lmax1,lmax2,lmin1,lmin2,rmax1,rmax2,rmin1,rmin2,mid;
if(left == right) {
*max1= *max2 = *min1 = *min2 = num[left];
}else if(left == right - 1) {
//此时控制一下,1为较小的那个,2为较大的那个
if(num[left] < num[right]) {
*max1= *min1 = num[left];
*max2= *min2 = num[right];
}else {
*max1= *min1 = num[right];
*max2= *min2 = num[left];
}
}else {
mid= (right + left)/2;
maxmin(left,mid,&lmax1,&lmax2,&lmin1,&lmin2);
maxmin(mid+ 1,right,&rmax1,&rmax2,&rmin1,&rmin2);
//此时应该是lmax1<lmax2,lmin1<lmin2,rmax1<rmax2,rmin1<rmin2
if(lmin2 < rmin1) {//左边两个比右边两个都小
*min1= lmin1;
*min2= lmin2;
}else {
if(rmin2 < lmin1) {//右边两个比左边两个都小
*min1= rmin1;
*min2= rmin2;
}else {//此时左边和右边最小的那个为两个最小值
*min1= lmin1;
*min2= rmin1;
}
}
if(lmax2 < rmax1) {//左边两个比右边两个都小
*max1= rmax1;
*max2= rmax2;
}else {
if(rmax2 < lmax1) {
*max1= lmax1;
*max2= lmax2;
}else {
*max1= lmax2;
*max2= rmax2;
}
}
}
}
int main()
{
int max1,max2,min1,min2;
printf("输入%d个数字:",N);
int i;
for(i = 0; i < N; i++) {
scanf("%d",&num[i]);
}
maxmin(0,N-1,&max1,&max2,&min1,&min2);
printf("最小的两个数是%d和%d,最大的两个数是%d和%d\n",min1,min2,max1,max2); return 0;
}
但是运行的时候,发现问题,当输入数据的顺序改变的时候,运行的结果可能会是错误的结果,
排查后发现发现出现这个现象的原因是,在划分子组的时候,最后将最大的数字10单独分成一组,此时按照代码逻辑max1 =max2 = 10,以至于后面无法找到第二大的数字,根据这个情况,此段代码无法达到目的,此时想起使用快速排序法将整串数字全部排序,再就可以顺利取出需要的数字。
代码如下:
#include <stdio.h>
#define N 10
void quicksort(int a[],int left,int right)
{
if(left >= right) {
return ;
}
int i = left;
int j = right;
int key = a[left];
while(i < j) {
while(i < j && key <= a[j]) {
j--;
}
a[i]= a[j];
while(i < j && key >= a[i]) {
i++;
}
a[j]= a[i];
}
a[i]= key;
quicksort(a,left,i- 1);
quicksort(a,i+1,right);
}
int main()
{
int i,num[N];
printf("输入%d个数字:",N);
for(i = 0; i < N; i++) {
scanf("%d",&num[i]);
}
quicksort(num,0,N-1);
printf("最小的两个数是%d和%d,最大的两个数是%d和%d\n",num[0],num[1],num[N-2],num[N-1]);
return 0;
}
2、利用分治法求一组数据的和
算法设计:
由于需要使用分治法,那么需要将这一串数据分割为若干个小串数据之后,将这些小串数据加起来,再逐级合并求整串数据的和即可。
代码如下:
#include <stdio.h>
#define N 10
int num[N];
void getSum(int left,int right, int *sum)
{
int lsum,rsum,mid;
if(left == right) {
*sum= num[left];
}else if(left == right - 1) {
*sum= num[left] + num[right];
}else {
mid= (left + right)/2;
getSum(left,mid, &lsum);
getSum(mid+ 1, right, &rsum);
*sum= lsum+rsum;
}
}
int main()
{
int i;
printf("输入%d个数字:",N);
for(i = 0; i < N; i++) {
scanf("%d",&num[i]);
}
int sum = 0;
getSum(0,N-1,&sum);
printf("sum= %d\n",sum);
return 0;
}