題目:求1+2+...+n,要求不能使用乘除法,for,while,if,else,switch,case等關鍵字及條件判斷語句(A?B:C)
阿新 • • 發佈:2019-01-10
這個問題本身沒有太多的實際意義,但不少面試官認為這個可以考察應聘者發散思維能力的題目,而發散思維能夠反映出應聘者知識面
的寬度,以及對程式設計相關技術理解的深度。
通常求1+2+..+n除了用公式n(n+1)/2之外,無外乎迴圈和遞迴兩種思想,由於已經明確限制for和while的使用,迴圈已經不能再用了。
遞迴函式也需要用if語句或者條件判斷語句來判斷是繼續遞迴下去還是終止遞迴,但現在題目已經補允許使用這兩種語句了。
一個型別,接著建立n個該型別的例項,那麼這個型別的建構函式將確定會被呼叫n次。我們可以將與累加相關的程式碼放到建構函式裡面裡。
解法二:利用虛擬函式求解
我們同樣也可以圍繞遞迴做文章。既然不能在一個函式中判斷是不是應該終止遞迴,那麼我們不妨定義兩個函式,一個函式充當遞迴函式
的角色,另一個函式處理終止遞迴的情況,我們需要做的是在兩個函式裡二選一。從二選一我們很自然的想到布林變數,比如值為true(1)的時候
呼叫第一個函式,值為false(0)的時候呼叫第二個函式。那現在的問題是如何把數值變數n轉換為布林值。如果對n連續做兩次反運算,即!!n
,那麼非零的n轉換為true,0轉換為false。
解法三:利用函式指標求解
在純C語言的程式設計環境中,我們不能使用虛擬函式,此時可以用函式指標來模擬,這樣程式碼可能還更加直觀一些。
Sum_Solution4<100>::N就是1+2+3+...+100的結果。當編譯器看到Sum_Solution4<100>時,就會為模板類Sum_Solution4以引數100生成該型別的程式碼。但以100為引數的型別需要得到以99為引數的型別,因為Sum_Solution4<100>::N = Sum_Solution4<99>::N + 100.這個過程會遞迴一直到引數為1的型別,由於該型別已經顯示定義,編譯器無須生成,遞迴編譯到此結束。由於這個過程是在編譯過程中完成的,因此要求輸入n必須是在編譯期間就能確定的常量,不能動態輸入,這時該方法最大的缺點。而且編譯器對遞迴編譯程式碼的遞迴深度是有限制的,也就是要求n不能太大。
的寬度,以及對程式設計相關技術理解的深度。
通常求1+2+..+n除了用公式n(n+1)/2之外,無外乎迴圈和遞迴兩種思想,由於已經明確限制for和while的使用,迴圈已經不能再用了。
遞迴函式也需要用if語句或者條件判斷語句來判斷是繼續遞迴下去還是終止遞迴,但現在題目已經補允許使用這兩種語句了。
解法一:利用建構函式求解
我們仍然圍繞迴圈做文章,迴圈只是讓相同的程式碼重複執行n遍而已,我們完全可以不用for和while來達到這個效果。比如我們先定義一個型別,接著建立n個該型別的例項,那麼這個型別的建構函式將確定會被呼叫n次。我們可以將與累加相關的程式碼放到建構函式裡面裡。
int Sum_Solution1(int n); class Temp { public: Temp() { ++N;Sum += N; } int GetSum() const { return Sum; } private: static int N; static int Sum; }; int Temp::N = 0; int Temp::Sum = 0; int main() { cout<<"sum="<<Sum_Solution1(100)<<endl; return 0; } int Sum_Solution1(int n) { Temp *a = new Temp[n]; delete []a; a = NULL; return a->GetSum(); }
解法二:利用虛擬函式求解
我們同樣也可以圍繞遞迴做文章。既然不能在一個函式中判斷是不是應該終止遞迴,那麼我們不妨定義兩個函式,一個函式充當遞迴函式
的角色,另一個函式處理終止遞迴的情況,我們需要做的是在兩個函式裡二選一。從二選一我們很自然的想到布林變數,比如值為true(1)的時候
呼叫第一個函式,值為false(0)的時候呼叫第二個函式。那現在的問題是如何把數值變數n轉換為布林值。如果對n連續做兩次反運算,即!!n
,那麼非零的n轉換為true,0轉換為false。
int Sum_Solution2(int n); class A; A *Array[2]; class A { public: virtual int Sum(int n) { return 0; } }; class B:public A { public: virtual int Sum(int n) { return Array[!!n]->Sum(n-1) + n; } }; int main() { cout<<"SUM="<<Sum_Solution2(100)<<endl; return 0; } int Sum_Solution2(int n) { A a; B b; Array[0] = &a; Array[1] = &b; int value = Array[1]->Sum(n); return value; }
解法三:利用函式指標求解
在純C語言的程式設計環境中,我們不能使用虛擬函式,此時可以用函式指標來模擬,這樣程式碼可能還更加直觀一些。
#include<stdio.h> typedef int (*fun) (int); int Sum_Solution3_Teminator(int n) { return 0; } int Sum_Solution3(int n) { fun f[2] = {Sum_Solution3_Teminator,Sum_Solution3}; return n + f[!!n]( n - 1 ); } int main() { printf("sum=%d\n",Sum_Solution3(100)); return 0; }
此外,我們還可以讓編譯器幫助完成類似於遞迴的運算。
template<int n>
struct Sum_Solution4
{
enum Value { N = Sum_Solution4<n-1>::N + n };
};
template<> struct Sum_Solution4<1>
{
enum Value{ N = 1 };
};
int main()
{
cout<<Sum_Solution4<100>::N<<endl;
return 0;
}
Sum_Solution4<100>::N就是1+2+3+...+100的結果。當編譯器看到Sum_Solution4<100>時,就會為模板類Sum_Solution4以引數100生成該型別的程式碼。但以100為引數的型別需要得到以99為引數的型別,因為Sum_Solution4<100>::N = Sum_Solution4<99>::N + 100.這個過程會遞迴一直到引數為1的型別,由於該型別已經顯示定義,編譯器無須生成,遞迴編譯到此結束。由於這個過程是在編譯過程中完成的,因此要求輸入n必須是在編譯期間就能確定的常量,不能動態輸入,這時該方法最大的缺點。而且編譯器對遞迴編譯程式碼的遞迴深度是有限制的,也就是要求n不能太大。