1. 程式人生 > >LeetCode演算法題-Pascal's Triangle II(Java實現)

LeetCode演算法題-Pascal's Triangle II(Java實現)

這是悅樂書的第171次更新,第173篇原創

01 看題和準備

今天介紹的是LeetCode演算法題中Easy級別的第30題(順位題號是119)。給定非負索引k,其中k≤33,返回Pascal三角形的第k個索引行。行索引從0開始。在Pascal的三角形中,每個數字是它上面兩個數字的總和。例如:

輸入: 2
輸出: [1,2,1]

輸入: 3
輸出: [1,3,3,1]

本次解題使用的開發工具是eclipse,jdk使用的版本是1.8,環境是win7 64位系統,使用Java語言編寫和測試。

02 第一種解法

昨天的那道題,是要求輸出所有層的資料,今天這題只要求具體某一行的資料,那是不是完全可以借用昨天的程式碼,最後返回具體某一層即可?這種解法當然是可行的,但是題目有個提示,要求空間複雜度是O(k),其中k代表某一行。而昨天的解法,最後的空間複雜度是O(numRows^2),那能不能把空間複雜度再縮小點?答案是可以的。

我們可以先將list中的每個元素初始值設為1,然後開始迴圈處理。外層迴圈控制層數,進入內層迴圈,改變具體位置元素的值。我們知道第k層第j(j>=1)個元素的值是第k-1層第j-1個元素和第j個元素之和,如果按照從左至右的順序,後面元素的值需要藉助前面元素的值的時候,是已經被改變過的值,最後的結果會失真,所以我們採用從右至左的順序設值。

注意:list.set(j, value)此方法是找到索引j所在元素,將value設值給它,不會新增元素,而add(value)是會新增元素的。

public List<Integer> getRow(int rowIndex) {
    List<Integer> list = new ArrayList<Integer>();
    for (int k = 0; k <= rowIndex; k++) {
        list.add(1);
    }
    for (int i = 1; i < rowIndex; i++) {
        for (int j = i; j > 0; j--) {
            list.set(j, list.get(j - 1) + list.get(j));
        }
    }
    return list;
}


03 第二種解法

同樣是第一種解法的思路,但是我想從左至右設值怎麼辦?

既然想要從左至右設值,那就需要左邊的資料是新的,而不是已經被計算過的新值,就需要把每次計算過的值往後移動一位,此時我們就無法在開始時就將list的初始元素值設為1,二是要在迴圈中新增新的元素,從而把舊的元素往後挪一位。

外層迴圈一方面是控制層數,另外一個方面就是每次在list的索引0位置新增元素1。只有當r等於2時,才會進入內層迴圈,此時list中至少有三個元素。

因為首尾元素都是1,所以內層迴圈的索引起始位置是1(第二位),依次往右重新設值。

public List<Integer> getRow2(int rowIndex) {
    List<Integer> list = new ArrayList<>();
    for (int r = 0; r <= rowIndex; r++) {
        list.add(0, 1);
        for (int i = 1; i < r; i++) { 
            list.set(i, list.get(i) + list.get(i + 1)); 
        }
    }
    return list;
}


04 第三種解法

我們還可以使用陣列來操作此題,思路與昨天的那道題大致類似。

外層迴圈每次建立一個新的陣列,陣列長度是i+1,並且此陣列的首尾元素是為1,然後開始內層迴圈,新陣列的第j(j>=1)位元素是上一陣列中的第j-1位和第j位元素之和。在結束內層迴圈後,還要將舊陣列指向經過內層迴圈賦值完後的新陣列。最後,將數組裡面的元素遍歷賦值給list。

public List<Integer> getRow3(int rowIndex) {
    List<Integer> list = new ArrayList<Integer>();
    int[] result = new int[1];
    for (int i = 0; i <= rowIndex; i++) {
        int[] next = new int[i + 1];
        next[0] = 1;
        next[i] = 1;
        for (int j = 1; j < i; j++) {
            next[j] = result[j - 1] + result[j];
        }
        result = next;
    }
    for (int in : result) {
        list.add(in);
    }
    return list;
}


05 驗證與測試

針對上面三種解法,選取部分隨機數字並測試不同解法耗時,程式碼如下:

public static void main(String[] args) {
    Easy_119_PascalTriangleII instance = new Easy_119_PascalTriangleII();
    int rowIndex = 3;
    long start = System.nanoTime();
    List<Integer> list = instance.getRow(rowIndex);
    long end = System.nanoTime();
    System.out.println("getRow---輸入:"+rowIndex+" , 輸出:"+list+" , 用時:"+(end-start)/1000+"微秒");
    long start2 = System.nanoTime();
    List<Integer> list2 = instance.getRow2(rowIndex);
    long end2 = System.nanoTime();
    System.out.println("getRow2---輸入:"+rowIndex+" , 輸出:"+list2+" , 用時:"+(end2-start2)/1000+"微秒");
    long start3 = System.nanoTime();
    List<Integer> list3 = instance.getRow3(rowIndex);
    long end3 = System.nanoTime();
    System.out.println("getRow3---輸入:"+rowIndex+" , 輸出:"+list3+" , 用時:"+(end3-start3)/1000+"微秒");
}

測試結果如下:

getRow---輸入:10 , 輸出:[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1] , 用時:73微秒
getRow2---輸入:10 , 輸出:[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1] , 用時:48微秒
getRow3---輸入:10 , 輸出:[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1] , 用時:24微秒


06 小結

解法一和解法二的思路類似,但是第二種解法稍快於解法一,第三種解法耗時最少,但是三種解法的空間複雜度都是O(k)。

以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支援!