O(n)空間複雜度,打印楊輝三角形的前n行

作小米的筆試題,給出一個整數n,求出它在楊輝三角形中第一次出現的行號。數組

想了半天,只能暴力法,從第1行開始找,一直找到第n行,若找獲得則返回行號,若找不到則返回n+1(由於第n+1行第2列確定是n)。固然,注意1是在第1行而不是第2行。更好的方法卻是沒想到,卻是折騰出了打印楊輝三角形前n行的最優方法。spa

若是空間不限制的話,就直接定義二維數組a[n][n],初始時a[0][0]=1,經過a[i][j]=a[i-1][j]+a[i-1][j-1]計算便可。這裏主要注意的就是邊界條件,每一行首尾必須爲1,只有夾在首尾中間的n-2個元素才適用於這個公式。指針

不事後來發現,只是打印的話,徹底能夠用2個一維數組來表示前一行和當前行。用vector表示的話就是v1和v2,每次經過v1計算出v2,打印完v2以後迭代(用v2給v1賦值)。後來發現除了vector可能從新分配的問題(這裏能夠vector初始化大小爲n來解決),用v2給v1賦值仍是很不必的。code

因而想到了能夠直接交換指針,v1和v2分配足夠大空間後就一直不改變大小,而後int* p1 = &v1[0]; int* p2 = &v2[0];而後每次迭代只須要std::swap(p1,p2)便可。blog

這裏至關於就是用了2片緩衝區(固定大小),其實知道行數上限的話,用2個數組表示也同樣。class

最後發現,緩衝區一片就夠了,這裏和copy/copy_backward的原理是相似的,只要從後往前遍歷的話就不會致使新元素覆蓋了舊元素從而計算出錯。原理

而後更進一步,楊輝三角形每一行是對稱的。因而咱們只須要取前半部分便可,對於奇數行取(n+1)/2個,對於偶數行取n/2個,按照C/C++中除法取整來看都是(n+1)/2個。遍歷

關鍵是,第2k+1行和第2k+2行的前半部分元素數量相同(好比第3行是1,2,第4行是1,3),而第2k+3行最後一個元素沒法直接計算。其實仔細看看就會發現規律,因爲對稱性,第2k+3行最後一個元素是第2k+2行最後一個元素的2倍。方法

到這裏就能夠寫代碼了,可是寫代碼的時候直接按這個思路來代碼會很醜,由於奇數行最後一個元素的計算方式和前面元素的計算方式不一致。一個優雅的寫法是,每當從偶數行跳到奇數行時,元素數量增長,而且初始時在末尾添加一個元素,再批量計算。im

好比第4行是1 3,那麼第5行初始化後就是1 3 3。而後a[2] = a[1] + a[0]; a[1] = a[1] + a[0];來計算。

// 打印楊輝三角形的前n行
void printPascalTriangle(int n)
{
    int maxrow = (n + 1) / 2;
    vector<int> v(maxrow + 1);
    v[0] = 1; // 哨兵節點
    bool bOddRow = true; // 奇數行則爲true
    int num = 0; // 須要計算的元素數量
    for (int row = 1; row <= n; row++)
    {
        if (bOddRow)
        {
            num++;
            v[num] = v[num - 1];
        }
        for (int i = num; i > 1; i--)
            v[i] += v[i - 1];

        for (int i = 1; i <= num; i++) // 正向打印
            cout << v[i] << " ";
        if (!bOddRow)  // 對於偶數行最後一個元素須要再打印一遍
            cout << v[num] << " ";
        for (int i = num - 1; i >= 1; i--) // 反向打印
            cout << v[i] << " ";
        cout << endl;
        bOddRow = bOddRow ? false : true; // 行號+1後改變了奇偶性
    }
}

相關文章
相關標籤/搜索