跳臺階

題目描述

假設你正在爬樓梯。需要 n 階你才能到達樓頂。

每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?

注意: 給定 n 是一個正整數。

示例1:

輸入: 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。

  1. 1 階 + 1 階
  2. 2 階

示例2:

輸入: 3
輸出: 3
解釋: 有三種方法可以爬到樓頂。

  1. 1 階 + 1 階 + 1 階
  2. 1 階 + 2 階
  3. 2 階 + 1 階

解題思路

 我們先舉例n=5,當我們邁出第一步的時候,有兩種解法,走1個臺階就剩下了4個臺階;走2個臺階就剩下了3個臺階。那麼我們就可以將剩下的4個臺階繼續按照上述的方法分解。
 所以n=5時,有兩種情況,分解成子問題,走n=4n=3的兩種情況之和。所以可以畫出圖解爲:
在這裏插入圖片描述
 但是如果按照上述的想法,計算上述題的時候,就是出現好多重疊的子問題。但是這樣的話會出現時間複雜度過大,不是一個好的算法。

解法一:暴力解法
class Solution {
    public int climbStairs(int n) {
        // 1. 當n=1時,有一種解法:爬1個臺階
        if(n == 1)
            return 1;
        if(n == 2)
            return 2;
        
        // 當n=5時,有兩種解法。
        // 先爬1個,計算climb(4)
        // 先爬2個,計算climb(3)
        return climbStairs(n-1) + climbStairs(n-2);
    }
}

 這個當我們運行的時候,就會發現當n=44時,該代碼就超出的時間限制,因爲這樣的遞歸做法計算了好多的重疊子問題(當然不同的電腦可能n的最大取值不同)。

解法二:自頂向下備忘錄法(記憶記錄法)

就是使用一個數組,將計算過得n記錄下來,當下一次要使用的時候,去查表,便可知道。這裏就需要一個數組,去保存,這就是使用空間去換取時間的方法。
在這裏插入圖片描述

class Solution {
    public int climbStairs(int n) {
        int[] array = new int[n + 1];
        // 先初始化數組
        for(int i = 0; i < array.length; i++){
            array[i] = -1; // 作爲標記,以此來判斷是否已經計算過了
        }
        return climb(n, array);
    }
    private int climb(int i, int[] arr){
        if(i <= 1)
            arr[i] = 1;
        if(i == 2)
            arr[i] = 2;
        // 如果數組中的值爲-1,沒有計算,需要計算
        if(arr[i] == -1)
            arr[i] = climb(i-1, arr) + climb(i-2, arr);
        
        // 走到這裏i個臺階的值已經計算過了直接返回
        return arr[i];
    }
}
解法三:自底向上的動態規劃法

 我們要計算n=5的值,因爲本類型題,是下一個答案依靠於上兩個答案,那我只需要從1開始計算到5就行了呀!
 這就是先求解子問題,再由子問題求解父問題。
所以代碼有了如下的改變,還是開闢數組保存前面子問題的解。

class Solution {
    public int climbStairs(int n) {
        if(n <= 0)
            return 0;
        if(n == 1)
            return 1;
        if(n == 2)
            return 2;
        int[] arr = new int[n+1];
        
        arr[1] = 1;
        arr[2] = 2;
        
        for(int i = 3; i <= n; i++){
            arr[i] = arr[i-1] + arr[i-2];
        }
        
        return arr[n];
    }
}
解法四:自底向上解法的優化版

 通過上面的解法我們可以知道,要求解父問題,我們可以先求解子問題,然後再由子問題求解父問題。所以我們在求解5的時候,只需要知道4和3就行了,不需要知道2、1,所以當我們消耗數組空間去存1、2時是不需要的步驟,所以我們會想到只用兩個int型的變量去存放父問題的兩個子問題的解就行了,所以上面的解法優化成了下面的解法:

class Solution {
    public int climbStairs(int n) {
        if(n == 1)
            return 1;
        if(n == 2)
            return 2;
        
        int num_i_2 = 1;
        int num_i_1 = 2;
        int num_i = 0;
        
        for(int i = 3; i <= n; i++){
            num_i = num_i_1 + num_i_2;
            num_i_2 = num_i_1;
            num_i_1 = num_i;
        }
        return num_i;
    }
}

我們會發現,這種解法與斐波那契數列求解第n項的代碼一樣。

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