算法设计与分析——第四篇,分治法

写在前面的话——

这次终于到分治法了,个人觉得分治法比较广泛的认知,就是快速排序法,不过似乎快速排序法只是用到了分治法的思想,但是并没有用到分治法,也不是很明白,反正这次出的题的话,能比较清楚地理解分治法是怎么样一种算法


第四篇——

话说我本来觉得第一题非常麻烦,虽然是用的课本的例题,但是发现这样写的话,有一定几率会出问题,也简单分析了一下,觉得还是快速排序法好用,所以写了两段代码,运行结果的图片懒得传了,但是前几天睡觉的时候,突然想起来这个题,仔细想了想,其实也不是无解的,我的第一段代码改改应该还是能够解出来的,为什么当时没有想明白。简单说明一下,之前发现的错误是,找出来的两个最大或者最小的数,可能是一样的,原因是分组的时候,一个数分成了一组,而这个数如果就是最大或者最小的时候,按照本身的这个代码,判断是有问题的,然后现在想想,因为最终是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;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值