- 求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
下面的思路来自《剑指offer》,考察的是发散思维。
-
思路一,构造函数结合类属型和类函数,跳转到代码。
题目本质上是把某个语句重复执行
N
次。可以创建一个Temp
类,该类有一个类属型N
和一个类属性SUM
,调用类的Reset()
函数的时候将两个类属型清零,调用类的构造函数的时候让N++
并且SUM+=N
,类似于学面向对象的时候记录总共创建了多少个实例一样。在主函数中创建大小为N
的该类的数组,然后把数组内存delete
掉,调用类的GetSum
函数获取SUM
类属型即可。如果把Temp
定义在Solution
类的里面的话要注意Temp
类的静态成员初始化要放在Solution
类的外面,但最好不要使用嵌套类,因为比较麻烦。-
声明为
static
的属性为类属性,声明为static
的成员函数为类函数,static
函数由于没有this
指针,因此仅能访问类的静态成员。 -
注意类的
static
属性必须在类外进行初始化且必须初始化。初始化时前面不加static,以免与一般静态变量或对象相混淆,静态数据成员初始化的格式:<数据类型> <类名>::<静态数据成员名> = <值>;
例如:
int Temp::Sum = 0;
-
-
思路二,利用数组充当函数选择器,虚函数结束递归,跳转到代码。
当
n
不为0的时候,对n
进行两次取反即!!n
的结果就是true
,而当n
为0
的时候!!n
的结果是false
,true
和false
作为下标的时候相当于1
和0
,因此可以利用!!n
作为数组的下标,选择调用哪个函数,当false
作为下标时选择结束递归的函数。这里利用对象数组和虚函数实现,具体的看代码更容易理解。 -
思路三,利用函数指针数组充当函数选择器,跳转到代码。
利用上面的原理,将函数指针放到数组中也可以实现,即函数指针数组。
-
思路四,模版函数或者模板类型,跳转到代码。
- 模板函数
template <int m> inline int SumTo() { return m + SumTo<m-1>(); } template <> inline int SumTo<1>() { return 1; }
这个方法要求在编译的时候就知道模板参数
m
的值,即m
必须是一个常量,不知道怎么在牛客上测试,在本地使用类似SumTo<5>()
的形式直接调用是可以的,但是把5
换成一个变量就不行了。- 模板类型(==目前没搞懂==)
template <int n> struct SumTo{ enum Value {N = n + SumTo<n-1>::N}; }; template <> struct SumTo<1>{ enum Value {N = 1}; };
同样要求模板参数
n
为常量,需要在编译时就确定该常量的值,不知道怎么在牛客上测试,但是在本地通过类似SumTo<5>::N
的方式可以得到结果。
下面的思路来自牛客网。
-
思路五,短路原理,跳转到代码。
非常巧妙,利用逻辑运算符的特点,
a && b
表达式中如果表达式a
为0
或者false
就不会计算表达式b
,使得当递归到0的时候不再往下递归,代码非常简单,看代码比较容易看懂。int Sum_Solution(int n) { n && (n += Sum_Solution(n-1)); return n; }
- 思路一,构造函数结合类属型和类函数,跳转到原理。
class Temp{
private:
static int N;
static int Sum;
public:
Temp() {N++; Sum += N;} // 构造函数
static void Reset() {N=0; Sum=0;} // 加上这个函数是为了可以多次重复测试
static int GetSum() {return Sum;} // 加上static,定义成类函数
};
int Temp::N = 0;
int Temp::Sum = 0;
class Solution {
public:
int Sum_Solution(int n) {
Temp::Reset();
Temp* a = new Temp[n];
delete []a;
a = NULL;
return Temp::GetSum();
}
};
- 思路一,构造函数结合类属型和类函数,嵌套类,仅内部类的静态成员初始化部分有所不同。
class Solution {
private:
class Temp{
private:
static int N;
static int Sum;
public:
Temp() {N++; Sum += N;} // 构造函数
static void Reset() {N=0; Sum=0;} // 加上这个函数是为了可以多次重复测试
static int GetSum() {return Sum;} // 加上static,定义成类函数
};
public:
int Sum_Solution(int n) {
Temp::Reset();
Temp* a = new Temp[n];
delete []a;
a = NULL;
return Temp::GetSum();
}
};
int Solution::Temp::N = 0; // 类成员初始化要放在类定义的后面
int Solution::Temp::Sum = 0;
- 思路二,利用数组充当函数选择器,虚函数结束递归,跳转到原理。
class A{
public:
virtual int Sum(int n) {return 0;}
};
A* ObjectArray[2];
class B: public A{
public:
virtual int Sum(int n) {return ObjectArray[!!n]->Sum(n-1) + n;}
};
class Solution {
public:
int Sum_Solution(int n) {
A a;
B b;
ObjectArray[0] = &a;
ObjectArray[1] = &b;
return b.Sum(n);
}
};
- 思路三,利用函数指针数组充当函数选择器,跳转到原理。
int sum0(int n); // 先声明函数,用于函数指针数组初始化
int sumN(int n);
int (*FP[2])(int) = {sum0, sumN}; // 函数指针数组
int sum0(int n) {return 0;}
int sumN(int n) {return n + FP[!!n](n-1);}
class Solution {
public:
int Sum_Solution(int n) {
return sumN(n);
}
};
或者借助函数的static
变量:
typedef int (*FP)(int);
int sum0(int n) {return 0;}
int sumN(int n) {
static FP fp[2] = {sum0, sumN}; // 创建函数指针数组
return n + fp[!!n](n-1);
}
class Solution {
public:
int Sum_Solution(int n) {
return sumN(n);
}
};
-
思路四,模板函数或者模板类型,本地可用,牛客因为不能在编译时确定模板参数的值,不知道怎么测试, 跳转到原理。
-
模板函数
template <int m> inline int SumTo() { return m + SumTo<m-1>(); } template <> inline int SumTo<1>() { return 1; }
-
模板类型
template <int n> struct SumTo{ enum Value {N = n + SumTo<n-1>::N}; }; template <> struct SumTo<1>{ enum Value {N = 1}; };
-
-
思路五,短路原理,跳转到原理。
class Solution {
public:
int Sum_Solution(int n) {
n && (n += Sum_Solution(n-1));
return n;
}
};