視頻地址:https://www.bilibili.com/video/BV1ix411f7aNjava
課程說明算法
前導技能編程
動態規劃(Dynamic Programming)數組
目標ide
課程開始函數
你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有必定的現金,影響你偷竊的惟一制約因素就是相鄰的房屋裝有相互連通的防盜系統,若是兩間相鄰的房屋在同一夜被小偷闖入,系統會自動報警。優化
給定一個表明每一個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的狀況下,可以偷竊到的最高金額。spa
示例 1:設計
輸入: [1,2,3,1] 輸出: 4 解釋: 偷竊 1 號房屋 (金額 = 1) ,而後偷竊 3 號房屋 (金額 = 3)。 偷竊到的最高金額 = 1 + 3 = 4 。
示例 2:code
輸入: [2,7,9,3,1] 輸出: 12 解釋: 偷竊 1 號房屋 (金額 = 2), 偷竊 3 號房屋 (金額 = 9),接着偷竊 5 號房屋 (金額 = 1)。 偷竊到的最高金額 = 2 + 9 + 1 = 12 。
思路:暴力破解法(遞歸算法)
public class Solution { public int solve(int idx,int[] nums){ if(idx<0){ return 0; } return Math.max(nums[idx]+solve(idx-2,nums),solve(idx-1,nums)); } public int rob(int[] nums){ return solve(nums.length-1,nums); } }
【注】代碼超時——\(O(2^n)\)
n-1->(n-3,n-4,n-5...)
n-2->(n-4,n-5...)
深度爲 \(n\)
\(O(2^n)\)
本質:遞歸
原問題(N)->子問題(N-1)->原問題(N)
最優子結構
重疊子問題
遞歸的意義:遞歸不是追求效率,遞歸追求的是咱們怎麼看待、拆解這個問題。
處理重疊子問題:(用一個數組來存結果)——計劃搜索
public class Solution { public static int[] result;//數組存結果 public int solve(int idx,int[] nums){ if(idx<0){ return 0; } if(result[idx]>=0){ return result[idx]; }//算過了直接從數組取值 result[idx]=Math.max(nums[idx]+solve(idx-2,nums),solve(idx-1,nums)); return result[idx]; } public int rob(int[] nums){ result=new int[nums.length]; for (int i = 0; i < nums.length; i++) { result[i]=-1; }//初始化數組置爲-1 return solve(nums.length-1,nums); } }
解:每一個狀態只算了一遍—— \(O(n)\)
套路
離散問題
最優子結構
遞歸改遞推(非遞歸):
自頂向下的解法
public class Solution { public static int[] result;//數組存結果 public int solve(int idx,int[] nums){ if(idx<0){ return 0; } if(result[idx]>=0){ return result[idx]; }//算過了直接從數組取值 result[idx]=Math.max(nums[idx]+solve(idx-2,nums),solve(idx-1,nums)); return result[idx]; } public int rob(int[] nums){ if(nums.length==0){ return 0; } if(nums.length==1){ return nums[0]; } result=new int[nums.length]; result[0]=nums[0]; result[1]=Math.max(nums[0],nums[1]); for (int idx = 2; idx < nums.length; ++idx) { result[idx]=Math.max(nums[idx]+result[idx-2],result[idx-1]); } return result[nums.length-1]; } }
斐波那契數列
N!
暴力破解法:
public class Solution { public int f(int n,int m){ if(n==0 || m==0){ return 0; } if(n==1 || m==1){ return 1; } return f(n-1,m)+f(n,m-1); } }
小偷有一個容量爲W的揹包,有n件物品,第i個物品價值vi,且重wi
目標:找到xi是的對於全部的xi={0,1}
\(sum(wi*xi)<=W\),而且\(sum(xi*vi)\)最大
套路:最大
Leetcode 322.零錢兌換
給定不一樣面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算能夠湊成總金額所需的最少的硬幣個數。若是沒有任何一種硬幣組合能組成總金額,返回 -1。
示例 1:
輸入: coins = [1, 2, 5], amount = 11 輸出: 3 解釋: 11 = 5 + 5 + 1
示例 2:
輸入: coins = [2], amount = 3 輸出: -1
暴力破解法:
public class Solution { public static int maxValue=10000000; public int search(int idx,int amount,int[] coins){ if(amount==0){ return 0; } if(amount<0){ return maxValue; } if(idx>=coins.length){ return maxValue; } return Math.min(search(idx,amount-coins[idx],coins)+1,search(idx+1,amount,coins)); } public int coinChange(int[] coins,int amount){ int res=search(0,amount,coins); if(res<maxValue){ return res; }else{ return -1; } } }
超時——優化(空間換時間)
public class Solution { public static int maxValue=10000000; public static int[][] f=new int[1000][10000]; public int search(int idx,int amount,int[] coins){ if(amount==0){ return 0; } if(amount<0){ return maxValue; } if(idx>=coins.length){ return maxValue; } if(f[idx][amount]>=0){ return f[idx][amount]; } f[idx][amount]=Math.min(search(idx,amount-coins[idx],coins)+1,search(idx+1,amount,coins)); return f[idx][amount]; } public int coinChange(int[] coins,int amount){ for (int i = 0; i < 1000; i++) { for(int j=0;j<10000;j++){ f[i][j]=-1; } } int res=search(0,amount,coins); if(res<maxValue){ return res; }else{ return -1; } } }
遞歸轉遞推(非遞歸)省空間
相似LeetCode 72
給你兩個單詞 word1 和 word2,請你計算出將 word1 轉換成 word2 所使用的最少操做數 。
你能夠對一個單詞進行以下三種操做:
插入一個字符
刪除一個字符
替換一個字符
示例 1:
輸入:word1 = "horse", word2 = "ros" 輸出:3 解釋: horse -> rorse (將 'h' 替換爲 'r') rorse -> rose (刪除 'r') rose -> ros (刪除 'e')
示例 2:
輸入:word1 = "intention", word2 = "execution" 輸出:5 解釋: intention -> inention (刪除 't') inention -> enention (將 'i' 替換爲 'e') enention -> exention (將 'n' 替換爲 'x') exention -> exection (將 'n' 替換爲 'c') exection -> execution (插入 'u')
僞代碼
X[n] 取哪些 Y[m] 取哪些 int search(int xi,int yi){ if(xi>n || yi>m){ return -1; } if(xi==n && yi==m){ return 0; } return Math.max( if(X[xi]==Y[yi]) search(xi+1,yi+1)+1, search(xi,yi+1), search(xi+1,yi) ); //search(xi,yi); }
暴力搜索
map[n][n]; map[i][j]-> i-j距離 boolean visit[n]; int search(int idx,int count){ if(count==n){ return 0; } int min=1000000; for(int i=0;i<n;i++){ if(!visit[i]){ visit[i]=true; int t=search(i,count+1)+map[idx][i]; if(t<min){ min=t; } } visit[i]=false;//復原 } return min; }
動態規劃 算法用到的題目存在不少套路
滾動數組,狀態壓縮,升維,單調性,四邊形不等式(高級套路)
先學套路,跳出套路
本質:先暴力,找冗餘,去冗餘