所以,分享最近疯狂面试遇到的两个面试题。
第一个题目很有意思,我之前遇到过它的初级版本,今天面试的时候见到了这个题目的升级版本,一时间我都吃不准最终的运行结果是什么了。废话少说,题目如下:
public class A
{
public static int X;
static A()
{
X = B.Y + 1;
}
}
public class B
{
public static int Y = A.X + 1;
static B()
{}
}
Console.WriteLine( "X={0},Y={1}", A.X, B.Y );
求输出的结果。
其实关于静态构造函数的题目,以前也见过,主要是考察静态构造函数和构造函数执行先后顺序的理解。这道题目倒是别出心裁,升级成了两个类型的静态构造函数和静态成员初始化执行先后顺序。
我们都知道静态成员初始化和静态构造函数编译后都是静态构造函数的一部分,而静态构造函数在类型被加载时执行,这道题目的头疼之处在于,并不是很明显能看出到底是A类加载在前还是B类加载在前。如果A类加载在前,则执行A类的静态构造函数的时候,必然会加载B类型,换言之在B.Y + 1这个表达式有结果前,B类型必须被加载,所以Y = 0(X默认为零) + 1 = 1,X = 1(Y被初始化) + 1 = 2。如果B类初始化在前,则结果恰恰相反。最后我认为A类将加载在前,结果应该是X=2,Y=1。但实际上心里并没底。
今天要说的第二个题目极为常见,简而言之就是用递归算法计算斐波那契数列的第N位,我不知道这种脑残的题目怎么能被一而再再而三的拿来考程序员,任何一个有基本素质的程序员都应该能够意识到,这种问题用迭代比递归要好上N倍。
在这里我首先给出那个该死的递归算法的解决方案:
int 该死的斐波那契递归算法计算函数( int index )
{
return index <= 2 ? 1 : 该死的斐波那契递归算法计算函数( index - 1 ) + 该死的斐波那契递归算法计算函数( index - 2 );
}
在这里我不得不再次疾呼,斐波那契数列用迭代计算要比递归好的多!
虽然这个题目很脑残,但稍微改一下,实现一个计算斐波那契数列计算效率最高的函数,却是一个非常有意思的问题。我认为要真正追求极端效率,就要从分析IL的角度入手,我暂时能想到下面这样的IL是计算效率最高的:
我们首先假设有两个int的局部变量
然后:
ldc.i4.1
ldc.i4.1 //推送两个1到堆栈上
ldc.i4.1
stloc.0 //再推送一个1,然后把它放到局部变量0。
:假想的Label
add //把栈上的两个数弹出相加,结果推入栈。
dup //把栈顶的计算结果复制一份
stloc.1 //然后把计算结果存到局部变量1。
ldloc.0 //把局部变量0的值入栈
ldloc.1 //把局部变量1的值入栈
stloc.0 //把栈顶数据(局部变量1的值)出栈并赋值给局部变量0。
猜猜现在堆栈还剩什么?运算结果是2,被复制一份,复制的拷贝弹出被存到局部变量1,局部变量0的值是1,入栈,堆栈上是2 1,局部变量1的值是2,也入栈,堆栈上是2 1 2,栈顶数据也就是2出栈再赋给局部变量0,堆栈上是2 1,局部变量0是2,局部变量1也是2。
然后这里跳转到add
br 假想的Label
跳转后,堆栈继续把2+1计算完毕,堆栈上是3,然后2被推进去,堆栈变成3 2,然后两个局部变量的值都被变换为3,又一次循环,堆栈上是5,3被推进去,成为5 3,周而复始计算斐波那契。
接下来的问题是,怎么用C#来体现这个IL逻辑?你能想到的最接近的方案是什么呢?
另外一个问题是,其实这个IL还算不上是效率最高的,应该还有效率更高的算法,你找出来没?
最后容我大吼一声,谁赏口饭吃吧。。。。。