zoukankan      html  css  js  c++  java
  • 分治法(求最大子序列和)

     1 //求出最大子序列 4 ,-3,5,-2,-1,2,6,-2 
     2 #include <stdio.h>
     3 int max (int a,int b,int c)
     4 {
     5     int ret;
     6     if(a > b)
     7     {
     8         ret = a;
     9     }else
    10     if(a <= b)
    11     {
    12         ret = b;
    13     }
    14     if(ret >= c)
    15     return ret;
    16     else
    17     return c;
    18 }
    19  int Findmaxsum(int box[],int size,int left,int right)      //参数(数组名,数组大小,左边界,右边界)
    20 {
    21     int mid = (right + left) / 2;
    22     if(left == right)                                        //分治递归要注意出口条件
    23     {
    24         return box[left];
    25     }
    26     int leftsum = Findmaxsum(box,size,left,mid );           //求出左半区最大子序列和 ,要有递归信任,不要纠结层层深入,假设该函数是正确的。 
    27     int rightsum = Findmaxsum(box,size,mid + 1,right);       //求出右半区最大子序列和
    28     int leftbordersum = 0;
    29     int rightbordersum = 0;
    30     int i;
    31     int thissum = 0;
    32     for(i = mid + 1 ;i <= right;i++)                         //求出含有中间分界点的右半区最大子序列和  (如果最大子序列横跨中间分界点,那么肯定包含中间分界点,)
    33     {
    34         thissum += box[i];
    35         if(rightbordersum < thissum)
    36         {
    37             rightbordersum = thissum;
    38         }
    39      } 
    40      thissum = 0;
    41      for(i = mid ;i >= left;i--)                          //求出含有中间分界点的左半区最大子序列和
    42      {
    43          thissum += box[i];
    44          if(leftbordersum < thissum)
    45          {
    46              leftbordersum = thissum;
    47          }
    48      }
    49      int midsum = leftbordersum + rightbordersum;               //横跨左右半区最大子序列和
    50      return max(midsum,leftsum,rightsum);                        //左半区最大子序列和,右半区最大子序列和,跨半区最大子序列和,三者中最大的为所求者
    51
    52     
    53 }
    54 int main ()
    55 {
    56     int box[8] = {4,-3,5,-2,-1,2,5,-2};
    57     int ret = 0;
    58     ret = Findmaxsum(box,8,0,7);
    59     printf("%d",ret);
    60     return 0 ;
    61  } 

    此算法时间复杂度为 O(NlogN).

    思考1:思考如何求得。

    可以先写出递推关系式,设T(n)为规模为n时程序运行的时间。

    1.观察到26,27行运用到了递归将问题规模缩小了一半且运用了两次,因此T(n) = 2T(n/2);

    2.第35至50得两个循环规模为n/2即O(n);因此T(n)=2T(n/2)+O(n),由于我们所求者也为大O所以可以写成T(n)=2T(n/2)+n;

    接下来可以有多种解决方法,此处介绍最笨但容易理解简单的迭代方法

    1.设迭代了k次

        T(n)=2T(n/2)+n

                =2*(2T(n/4)+n/2)+n=2^2T(n/2^2)+2n

                ...

        =2^k T(n/2^k) + kn

    由于最后要迭代到基准情形即: n/2^k = 1;因此我们可得k = logn;

    2.将k带入:

        =2^(logn)T(n/2^logn)+nlogn;

        =nT(1) + nlogn

    显然可以求得:O(nlogn);

    思考2:突然想到:为什么二分查找的时间复杂度O(logn)?同样本质是二分为什么此处O(nlogn)多出n?

      二者方法都为“分”,但是二分查找“分”后并不需要"合",而此处的分治法需要“分”求得结果后再“合”。

    可以这么想,二分查找O(logn)不难理解,但其本质在于只要找到所求元素,对于其他元素并不关心,

    每次递归中不符合条件的元素,直接不需要程序继续处理。程序笔直的朝着缩小范围的目的地前进,最后

    找到一个目标。

      而分治“分”后其实并没有将其中的某一半舍弃,他其实需要处理到每一个元素。简而言之,二分法“分”

    后直接将一半舍弃,分治需要将每一半都处理。分到最后每单个元素都处理过了,然后容易想到既然一个元素

    是O(logn),呢么所有n个元素在其前加个n得到O(nlogn)也就自然而然了。

    思考3:究竟为什么问题“分”后治之会将时间复杂度降低?

      以求最小子序列和为例,用最普通的暴力搜索,第一个与后面n-1个元素做比较,第二个元素n-2次....共[(1+n-1)*(n-1)]/2次;

    他的时间复杂度为O(N^2).可将规模为n时计算量近似看成n^2次,用分治法将问题规模缩小一半,则n/2时,计算量n^2/4。由于

    有两半增加了问题数量所以乘以2,所以n^2/4 * 2 = n^2/2;比原来计算量n^2减少n^2/2次。由此减少了计算量,降低了时间复杂度。

    关键点在于N^2是个平方阶,将n规模缩小计算量会以平方阶下降,虽然乘以n(线性阶)但与下降相比还是少。

    本人还才疏学浅,如有错误的地方非常感谢大家的指正。!

  • 相关阅读:
    CentOS安装配置Tomcat-7
    CentOS搭建VSFTP服务器
    使用DDMS测试安卓手机APP的性能(android)
    Linux常见问题及解决方案
    数据库版本控制工具:NeXtep Designer
    身份证号码编码规则
    无网络安装mysql步骤
    HTTP协议详解
    Jenkins持续集成构建
    Gulp和Webpack对比
  • 原文地址:https://www.cnblogs.com/Ponytai1/p/5871021.html
Copyright © 2011-2022 走看看