劍指Offer學習總結-求1+2+ .... +n
劍指Offer學習總結-求1+2+ …. +n
求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};
};