1. 程式人生 > >劍指Offer學習總結-求1+2+ .... +n

劍指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};
};