DSAA之最大子序列之和问题(四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LoveStackover/article/details/79978867

1. 算法4

  该算法比较巧妙,先从一个角度出发:最大子序列首位元素不能小于0,然后扩展概念:最大子序列前几项之和不应该小于0,根据这个准则并不能写出该算法,还要牢记子序列元素的index是从左至右,或者说从小到大。
  根据以上几点,如果我们不玩后验逻辑,(根据结果去理解过程)此时我们写这个程序应该是抱着找到最大子序列头几项之和不小于0的想法。修改maxsubsquencesum如下:

int maxsubsquencesum(int * ptr,int n){
    int i,j,k,thissum,topsquencemaxsum=0;
    thissum=0;
    for(i=0;i<n;i++){

        thissum+=*(ptr+i);
            if(thissum > 0)
                topsquencemaxsum=thisum;
        //直接舍弃前面和小于0的几项
        else if(thissum <0)
            thissum=0;
    }
    return topsquencemaxsum;
}

  很显然该算法的时间复杂度为O(n),特别的上面该算法的实现只能解最大子序列之和大于0的情况。
  

2. Logarithms in the Running Time

  两个估计算法运行时间的准则:

  • An algorithm is O(logn) if it takes constant (O(1)) time to cut the problem size by a fraction (which is usually 1/2 ).
  • On the other hand, if constant time is required to merely reduce the problem by a constant amount (such as to make the problem smaller by 1), then the algorithm is O(n).

  对分查找和二叉树查找很像,假设数列已经按照顺序排列,寻找一个元素是否位于该数列中,其算法时间复杂度为O(logn),和《DSAA》不同一种递归的实现思路如下:

char binarysearch(int * ptr, int x, int start, int end){
     int center;
     //基准
     if(start == end)
         if(*(ptr+start) == x)
             return x;
         else 
             return 'a';
     //递进基准
     center=(start + end)/2;
     if(*(ptr+center) > x)
         binarysearch(ptr, x, start, center);
     else if(*(ptr+center) < x)
         binarysearch(ptr, x, center+1, end);
     else
         return x;
}

  结果如下:

[root@localhost ~]# ./2_2           
5
1 2 3 4 5
please input your goal:
1
find 1
[root@localhost ~]# ./2_2
5
1 2 3 4 5
please input your goal:
0
can't find 0
[root@localhost ~]# 

  每次递归的执行语句运行时间为O(1),运行次数f2f=n,变换得到f=log2n=logn,故该算法的复杂度为T(n)=O(logn1)=O(logn)

2.2 Euclid’s algorithm

  欧几里德算法为什么是正确的证明,笔者就直接略过,直接记结论,两个数的最大公约数可以如下所示的求解,同样区别于《DSAA》,用递归的思路:


int gcd(int x, int y){
   int current_gcd;
   static int pre_gcd;
   //此时在基准之前进行执行该语句
   current_gcd=x%y;
   if(current_gcd == 0)
        return pre_gcd;
   pre_gcd=current_gcd;
   gcd(y,current_gcd);
}

  结果:

[root@localhost data_struce]# ./2_3
50 15
gcd: 5
[root@localhost data_struce]# ./2_3
1989 1590
gcd: 3
[root@localhost data_struce]# 

  在时间复杂度方面,根据:

THEOREM 2.1.
If m > n, then m mod n < m/2.

  该定理的证明比较简单不赘述,根据该定理,可以假设上面的递归最多执行logn次,当然并不准确,并且远远扩大了递归次数,所以最坏的运行时间小于O(logn)

2.3 Exponentiation

  一般提到取幂运算,直接会想到时间复杂度为O(n)的常规思路,但是如果按照次数拆分,我们最多需要logn次乘法,显然领先前面的常规思路:

int power(int x, int n){
    if( n == 0)
        return 1;
    if(IsEven(n))
        return power(x*x,n/2);
        else
            return power(x*x,n/2)*x;        
} 

  《DSAA》还提出了一种错误的写法:

int power(int x, int n){
    if( n == 0)
        return 1;
    if(IsEven(n))
            return power(x,n/2)*power(x,n/2);
    else
            return power(x*x,n/2)*x;        
} 

  该写法的时间复杂度将不是O(logn),可以从乘法次数分析,递归的次数将增加很多,具体分析比较麻烦,可以稍微思考下return power(x,n/2)*power(x,n/2)每次调用都将多产生一个时间复杂度为O(log(n/2))的子问题。

3. 最后

  还是很有意思,起码比较烧脑,如果笔者以前是计科专业,必然走向ACM的道路,还是蛮喜欢的。

阅读更多
想对作者说点什么?
相关热词

博主推荐

换一批

没有更多推荐了,返回首页