簡單動態規劃題解總結(leetcode探索 初級算法)

爬樓梯

題目:

假設你正在爬樓梯。需要 n 階你才能到達樓頂。 每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是一個正整數。

示例 1:

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

  1. 1 階 + 1 階
  2. 2 階 示例 2:

示例2:

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

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

思路一:簡單遞歸

首先,考慮特殊情況,即階梯數爲0,1,2時,分別返回值爲0,1,2。
然後我們只需要返回n-1 和n-2即可,讓他逐層往下遞歸。(爬樓梯每次只趴1或2,從總階數倒過來看,總階數的方法數由n-1階數和n-2階數的方法數來決定)
即:

public int climbStairs(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 2;
        return climbStairs(n-1)+climbStairs(n-2);
   }

此方法性能上有很大的浪費,因爲,在進行遞歸時,有很大一部分計算是重複了的,比如,在進行計算climbStairs(n-1) 時,它要遞歸計算climbStairs(n-2)+climbStairs(n-3);而在進行計算climbStairs(n-2)時,它其實已經被算過一次了,但是還要重複計算。

思路二:hashmap

由思路一知道,計算時,會有重複計算的情況,所以可以想辦法把一些值存起來,以後就不需要再計算。

    public int climbStairs(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 2;
        return climbStairs(n-1)+climbStairs(n-2);
        
        Map<Integer,Integer> map= new HashMap<>();
        return climbStairs(n,map);
    }
    public int climbStairs(int n,Map<Integer,Integer> map){
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 2;
        if(map.containsKey(n)){
            return map.get(n);
        }else{
            int val = climbStairs(n-1,map)+climbStairs(n-2,map);
            map.put(n,val);
            return val;
        }
    }

思路三:動規

每一個的階數是前面倆的和F(n)=F(n-1)+F(n-2)
這不就是斐波那契數列。
這樣可以從前往後推。

public int climbStairs(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 2;
        int a=1,b=2,temp=0;
        for(int i=2;i<n;i++){
            temp = a+b;
            a=b;
            b=temp;
        }
        return temp;
    }

買賣股票最佳時機

題目:

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。

注意:你不能在買入股票前賣出股票。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
注意利潤不能是 7-1 = 6, 因爲賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。

示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤爲 0。

思路一:暴力法

對於此題可以通過兩重循環,依次比較更新最大的利潤值。

思路二:保存最小值、最大利潤

可以注意到,最大利潤肯定是,最小值在左,最大在右。
那麼我們只需要一次遍歷,在這一次遍歷中,要存儲最小值,並維護一個最大利潤值,當一遍遍歷的時候,當前值-最小值>最大利潤值時,替換最大利潤

public int maxProfit(int[] prices) {
        if(prices==null || prices.length==0){
            return 0;
        }
        int profit=0, min=prices[0];
        for(int i=0;i<prices.length;i++){
            if(min>prices[i])
                min = prices[i];
            if(prices[i]-min>profit){
                profit = prices[i]-min;
            }
        }
        return profit;
    }

最大子序和

思路一:暴力法

一位學長給我說過,如果刷題的時候一時間想不出來怎麼做,可以先從最簡單暴力法開始。
對於這個題,可以通過三層循環。
對於 [7,1,5,3,6,4]

  • 第一層,從7遍歷到4
  • 第二次,從上層的那個數的下一個數開始,遍歷到4
  • 第三層,在一二層之間兩個數進行遍歷相加,並存儲爲max
    最後返回max。

思路二:改進暴力

很明顯,上面暴力法中第三層循環時多餘的,因爲在第二層遍歷的時候,就可以存儲最大值。

思路三:動規

參考文章:leetcode-最大子序和(四種)

在一遍遍歷中,只要每次都保存着之前的最大值即可。

  • 當臨時最大值小於0,直接換成數組中的值
  • 當前值大於0的話,就同nums[i]相加

設sum[i]爲以第i個元素結尾且和最大的連續子數組。假設對於元素i,所有以它前面的元素結尾的子數組的長度都已經求得,那麼以第i個元素結尾且和最大的連續子數組實際上,要麼是以第i-1個元素結尾且和最大的連續子數組加上這個元素,要麼是隻包含第i個元素,即sum[i] = max(sum[i-1] + a[i], a[i])。可以通過判斷sum[i-1] + a[i]是否大於a[i]來做選擇,而這實際上等價於判斷sum[i-1]是否大於0。由於每次運算只需要前一次的結果,因此並不需要像普通的動態規劃那樣保留之前所有的計算結果,只需要保留上一次的即可,因此算法的時間和空間複雜度都很小

public int maxSubArray(int[] nums) {// 動態規劃法
        int sum=nums[0];
        int n=nums[0];
        for(int i=1;i<nums.length;i++) {
            if(n>0)n+=nums[i];
            else n=nums[i];
            if(sum<n)sum=n;
        }
        return sum;
    }https://blog.csdn.net/zwzsdy/article/details/80029796

打家劫舍

題目:

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。

示例 1:

輸入: [1,2,3,1]
輸出: 4
解釋: 偷竊 1 號房屋 (金額 = 1) ,然後偷竊 3 號房屋 (金額 = 3)。
偷竊到的最高金額 = 1 + 3 = 4 。

示例 2:

輸入: [2,7,9,3,1]
輸出: 12
解釋: 偷竊 1 號房屋 (金額 = 2), 偷竊 3 號房屋 (金額 = 9),接着偷竊 5號房屋 (金額 = 1)。
偷竊到的最高金額 = 2 + 9 + 1 = 12 。

這是一道典型的動態規劃題,類似01揹包,對於一家屋子偷或不偷。
dp[i]=max(dp[i-1],dp[i-2]+nums[i])

解法

 public int rob(int[] nums) {
        if(nums.length==0 || nums==null){
            return 0;
        }
        int[] results = new int[nums.length+1];
        results[0] = 0;
        results[1] = nums[0];
        for(int i=2;i<=nums.length;i++){
            results[i]=Math.max(results[i-2]+nums[i-1],results[i-1]);
        }
        return results[nums.length];
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章