剑指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};
};