剑指Offer学习总结-求1+2+ .... +n

剑指Offer学习总结-求1+2+ …. +n

本系列为剑指Offer学习总结,主要是代码案例的分析和实现:
书籍链接:http://product.dangdang.com/24242724.html
原作者博客:http://zhedahht.blog.163.com/blog/static/254111742011101624433132/
原作者博客链接有完整的项目代码下载。


求1+2+ …. +n

题目

题目:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

常规的解法

每个人对这个题目都比较熟悉,也都会求解。每个人能够想到的方法有
1)递归
2)循环累加求和。
3)直接用等差数列求和公式

递归改造的解法

递归方法,里边我们主要是要利用if语句,判断最后是否达到边界条件。
我们可以使用n本身来做判断,n&&上一次的结果。 如果n不等于0,上一次的结果才会执行。

class Solution {  
public:  
    int Sum_Solution(int n) {  
        int res=n;  
        res&&(res += Sum_Solution(n-1));  
        return res;  // n=0时,累加和为0  
    }  
};  

二维数组的解法

由于计算结果比较容易得到,即为 n(n+1)/2,故若选取占用1个Byte的bool(或char)型数组arr[n][n+1],则sizeof(char)*n(n+1)>>1即为所求,如果选取占用4个Byte的int型数组arr[n][n+1],则sizeof(int)*n(n+1)>>3即为所求。不过由于char可能会在不同的编译器上占的字节数不一样,不建议使用。本人选用了int型二维数组来解决。

class Solution {  
public:  
    int Sum_Solution(int n) {  
        int a[n][n+1];  
        return sizeof(a)>>3;  
    }  
}; 

利用构造函数的解法

我们仍然围绕循环做文章。 循环只是让相同的代码重复执行 H 遍而已,我们完全可以不用 for 和 while 来迖到这个效果。
比如我们先定义一个类型,接着创建 n 个该类型的实例, 那么这个类型的构造函数将确定会被调用n次。
我们可以将与累加相关的代码放到构造函数里。

class Temp
{
public:
    //累加这个全局变量 并且这个N是每次都发生自增变化的
    Temp() { ++ N; Sum += N; }

    static void Reset() { N = 0; Sum = 0; }
    static unsigned int GetSum() { return Sum; }

private:
    static unsigned int N;
    static unsigned int Sum;
};

unsigned int Temp::N = 0;
unsigned int Temp::Sum = 0;

unsigned int Sum_Solution1(unsigned int n)
{
    Temp::Reset();

    //创建一个Temp变量的数组 ,里边包含N个Temp相当于执行了N次Temp的构造函数
    Temp *a = new Temp[n];
    delete []a;
    a = NULL;

    return Temp::GetSum();
}

虚函数的解法

我们冋样也W以围绕递归做文章。 既然不能在一个函数中判断是不是应该终止递归, 那么我们不妨定义两个函数,
一个函数充当递归函数的角色,另一个函数处理终止递归的情况, 我们需要做的就是在两个函数里二选一。
从二选一我们很自然地想到布尔变量, 比如值为 ture ( 1 ) 的时候调用第一个函数, 值为 false (0) 的时候调用第二个函数。
那现在的问题是如何把数值变量 n 转换成布尔值。 如果对 n 连续做两次反运算, 即!!n,
那么非零的转换为 true, 0 转换为 false。

class A;
A* Array[2];
//存储两个虚方法 
class A
{
public:
    virtual unsigned int Sum (unsigned int n) 
    { 
        return 0; 
    }
};

class B: public A
{
public:
    virtual unsigned int Sum (unsigned int n) 
    {   
        return Array[!!n]->Sum(n-1) + n; 
    }
};

int Sum_Solution2(int n)
{
    A a;
    B b;
    Array[0] = &a;
    Array[1] = &b;

    int value = Array[1]->Sum(n);

    return value;
}

函数指针的解法

在纯 C 语言的编程环境中, 我们不能使用虚函数, 此时可以用函数指针来模拟, 这样代码可能还更加直观一些

typedef unsigned int (*fun)(unsigned int);

unsigned int Solution3_Teminator(unsigned int n) 
{
    return 0;
}

unsigned int Sum_Solution3(unsigned int n)
{
    static fun f[2] = {Solution3_Teminator, Sum_Solution3}; 
    return n + f[!!n](n - 1);
}

模板类型来求解

Sum_Solution4<100>::N 就是 1+2+…+100 的结果 a 当编译器看到 Sum
Solution4<100>时, 就会为模板类 Sum Solution4 以参数 100 生成该类型的代码。
但以 100 为参数的类型需要得到以 99 为参数的类型,
因为 SumSolution4<100>::N= Sum Solution# <99>::N+100。
这个过程会递归一直到参数为 1 的炎型, 由于该类型d经显式定义, 编译器无须生成, 递归编译到此结柬。
由于这个过程是在编译过程中完成的, 因此要求输入 n 必须是在编译期间就能确定的常量,
不能动态输入, 这是该方法最大的缺点。 而且编译器对递归编译代码的递归深度是有限制的, 也就是要求 n 不能太大。

template <unsigned int n> struct Sum_Solution4
{
    enum Value { N = Sum_Solution4<n - 1>::N + n};
};

template <> struct Sum_Solution4<1>
{
    enum Value { N = 1};
};

template <> struct Sum_Solution4<0>
{
    enum Value { N = 0};
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值