LeetCode 力扣 119. 楊輝三角 II

題目描述(簡單難度)

118 題 一樣,依舊是楊輝三角。區別在於之前是輸出所有層的數,這道題只需要輸出第 k 層的數。

解法一

118 題 一樣,我們只需要一層一層的求。但是不需要把每一層的結果都保存起來,只需要保存上一層的結果,就可以求出當前層的結果了。

public List<Integer> getRow(int rowIndex) {
    List<Integer> pre = new ArrayList<>();
    List<Integer> cur = new ArrayList<>();
    for (int i = 0; i <= rowIndex; i++) {
        cur = new ArrayList<>();
        for (int j = 0; j <= i; j++) {
            if (j == 0 || j == i) {
                cur.add(1);
            } else {
                cur.add(pre.get(j - 1) + pre.get(j));
            } 
        }
        pre = cur;
    }
    return cur;
}

參考 這裏,其實我們可以優化一下,我們可以把 preList 省去。

這樣的話,cur每次不去新建 List,而是把cur當作pre

又因爲更新當前j的時候,就把之前j的信息覆蓋掉了。而更新 j + 1 的時候又需要之前j的信息,所以在更新前,我們需要一個變量把之前j的信息保存起來。

public List<Integer> getRow(int rowIndex) {
    int pre = 1;
    List<Integer> cur = new ArrayList<>();
    cur.add(1);
    for (int i = 1; i <= rowIndex; i++) {
        for (int j = 1; j < i; j++) {
            int temp = cur.get(j);
            cur.set(j, pre + cur.get(j));
            pre = temp;
        }
        cur.add(1);
    }
    return cur;
}

區別在於我們用了 set 函數來修改值,由於當前層比上一層多一個元素,所以對於最後一層的元素如果用 set 方法的話會造成越界。此外,每層的第一個元素始終爲1。基於這兩點,我們把之前j == 0 || j == i的情況移到了for循環外進行處理。

除了上邊優化的思路,還有一種想法,那就是倒着進行,這樣就不會存在覆蓋的情況了。

因爲更新完j的信息後,雖然把j之前的信息覆蓋掉了。但是下一次我們更新的是j - 1,需要的是j - 1j - 2 的信息,j信息覆蓋就不會造成影響了。

public List<Integer> getRow(int rowIndex) {
    int pre = 1;
    List<Integer> cur = new ArrayList<>();
    cur.add(1);
    for (int i = 1; i <= rowIndex; i++) {
        for (int j = i - 1; j > 0; j--) {
            cur.set(j, cur.get(j - 1) + cur.get(j));
        }
        cur.add(1);//補上每層的最後一個 1 
    }
    return cur;
}

解法二 公式法

如果熟悉楊輝三角,應該記得楊輝三角其實可以看做由組合數構成。

根據組合數的公式,將(n-k)!約掉,化簡就是下邊的結果。

Cnk=n!/(k!(nk)!)=(n(n1)(n2)...(nk+1))/k!C^k_n = n!/(k!(n-k)!) = (n*(n-1)*(n-2)*...(n-k+1))/k!

然後我們就可以利用組合數解決這道題。

public List<Integer> getRow(int rowIndex) {
    List<Integer> ans = new ArrayList<>();
    int N = rowIndex;
    for (int k = 0; k <= N; k++) {
        ans.add(Combination(N, k));
    }
    return ans;
}

private int Combination(int N, int k) {
    long res = 1;
    for (int i = 1; i <= k; i++)
        res = res * (N - k + i) / i;
    return (int) res;
}

參考 這裏,我們可以優化一下。

上邊的算法對於每個組合數我們都重新求了一遍,但事實上前後的組合數其實是有聯繫的。

$C_nk=C_n{k-1}\times(n-k+1)/k $

代碼的話,我們只需要用pre變量保存上一次的組合數結果。計算過程中,可能越界,所以用到了long

public List<Integer> getRow(int rowIndex) {
    List<Integer> ans = new ArrayList<>();
    int N = rowIndex;
    long pre = 1;
    ans.add(1);
    for (int k = 1; k <= N; k++) {
        long cur = pre * (N - k + 1) / k;
        ans.add((int) cur);
        pre = cur;
    }
    return ans;
}

這道題其實還是比較簡單的,只是優化的兩種方法是比較常用的,一種就是用pre變量將要被覆蓋的變量存起來,另一種就是倒着進行。另外求組合數的時候,要防止int的溢出。

更多詳細通俗題解詳見 leetcode.wang

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章