发散思维的特点是思维活动的多向性和交通性,也就是我们在思考问题时注重运用多思路、多方案、多途径地解决问题,我们可以从不同的方向、侧面和层次,采用探索、转换、迁移、组合和分解等方法,提出多种创新的解法。
求1+2+3+...+n
求1+2+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。这道题出自剑指offer的面试题,是一道不错的能够考察应聘者发散思维能力的题目,对于快要毕业的我们,的确是一个挑战。
之前我们求解这样的问题无非就是套用公式,或者循环,递归求解,既然限制了这些方法的实现,那是否还有更为巧妙的方法呢?下面为大家列举出四种方法。
1.利用构造函数求解
我们先定义一个类型,接着创建n个该类性的实例,那么这个类型的构造函数将确定会被调用n次。我们可以将与累加相关的代码放到构造函数中,下面代码正是基于这个思路。
class Sum
{
public:
Sum()
{
++n;
sum += n;
}
static void Resert()
{
n = 0;
sum = 0;
}
static unsigned int GetSum()
{
return sum;
}
private:
static unsigned int n;
static unsigned int sum;
};
unsigned int Sum::n = 0;
unsigned int Sum::sum = 0;
unsigned int SumSolution(unsigned int n)
{
Sum::Resert();
Sum *tmp = new Sum[n];
delete[] tmp;
tmp = NULL;
return Sum::GetSum();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
classSum
{
public:
Sum()
{
++n;
sum+=n;
}
staticvoidResert()
{
n=0;
sum=0;
}
staticunsignedintGetSum()
{
returnsum;
}
private:
staticunsignedintn;
staticunsignedintsum;
};
unsignedintSum::n=0;
unsignedintSum::sum=0;
unsignedintSumSolution(unsignedintn)
{
Sum::Resert();
Sum *tmp=newSum[n];
delete[]tmp;
tmp=NULL;
returnSum::GetSum();
}
2.利用虚函数求解
既然不能再一个函数中判断是不是应该终止地柜,那么我们不妨定义两个函数,一个函数充当递归函数的角色,另一个函数终止递归的情况,利用布尔变量,值为true时调用第一个函数,值为false调用第二个函数。如果对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 SumSolution(int n)
{
A a;
B b;
Array[0] = &a;
Array[1] = &b;
int value = Array[1]->Sum(n);
return value;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
classA;
A*Array[2];
classA
{
public:
virtualunsignedintSum(unsignedintn)
{
return0;
}
};
classB:publicA
{
public:
virtualunsignedintSum(unsignedintn)
{
returnArray[!!n]->Sum(n-1)+n;
}
};
intSumSolution(intn)
{
Aa;
Bb;
Array[0]=&a;
Array[1]=&b;
intvalue=Array[1]->Sum(n);
returnvalue;
}
这种思路是用虚函数来实现函数的选择。当n不为零时,调用函数B::Sum,当n等于零时,调用函数A::Sum。
3.利用函数指针求解
在C语言的范畴内,是没有虚函数这个概念的,因此我们可以使用函数指针来模拟,这样代码可能还更加直观一些。
typedef unsigned int(*fun)(unsigned int);
unsigned int SolutionTeminator(unsigned int n)
{
return 0;
}
unsigned int SumSolution(unsigned int n)
{
static fun f[2] = { SolutionTeminator,SumSolution };
return n + f[!!n](n - 1);
}
1
2
3
4
5
6
7
8
9
10
typedefunsignedint(*fun)(unsignedint);
unsignedintSolutionTeminator(unsignedintn)
{
return0;
}
unsignedintSumSolution(unsignedintn)
{
staticfunf[2]={SolutionTeminator,SumSolution};
returnn+f[!!n](n-1);
}
4.利用模板类型求解
既然不能使用递归来求解,那么我们让编译器帮助完成类似于递归的计算,比如下面的代码:
template
struct SumSolution
{
enum Value
{
N = SumSolution::N + n
};
};
template <>
struct SumSolution<1>
{
enum Value
{
N = 1
};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template
structSumSolution
{
enumValue
{
N=SumSolution::N+n
};
};
template<>
structSumSolution<1>
{
enumValue
{
N=1
};
};
SumSolution<100>::N就是1+2+...+100的结果,当编译器看到SumSolution<100>时,就会为模板类SumSolution以参数100生成该类性的代码,但以100为参数的类型需要得到以99为参数的类型,因为SumSolution<100>::N=SumSolution=SumSolution<99>::N+100。这个过程会递归一直到参数为1的类型,由于该类型已经显示定义,编译器无须生成,递归编译到此结束。
但是这种方法的弊端就在于,输入n必须是在编译期间就能确定的常量,不能动态输入,而且编译器对递归编译代码的递归深度是有限制的,也就是说n不能太大。