題意:面試題10- I. 斐波那契數列
思路:最基礎的動態規劃題。數據量比較大的時候不能使用遞歸,會報StackOverFlow Exception,最優的方式是迭代計算。java
class Solution { public int fib(int n) { if (n <= 1) { return n; } int a = 0; int b = 1; int index = 2; int tmp; while (index <= n) { tmp = (a + b) % 1000000007; a = b; b = tmp; index ++; } return b; } }
題意:面試題10- II. 青蛙跳臺階問題
思路:同斐波拉契數列問題,最基礎的動態規劃問題。面試
class Solution { public int numWays(int n) { if (n == 0 || n == 1) { return 1; } int a = 1; int b = 1; int c = 0; int i = 2; while (i <= n) { c = (a + b) % 1000000007; a = b; b = c; i++; } return c; } }
題意:面試題14- I. 剪繩子
思路1:dp[i]表示長度爲i的繩子,可以獲得的最大乘積數。dp[i+1]的計算方式爲,每次能夠剪掉1~i的長度j,其他的繩子能夠剪(即dp[i+1-j]),也能夠不剪(即i+1-j),即
dp[i+1] = Math.max(dp[i+1-j] * j, (i+1-j) * j), 其中j \(\in\) [1, i]正則表達式
class Solution { public int cuttingRope(int n) { if (n == 2) { return 1; } int[] dp = new int[n + 1]; dp[2] = 1; for (int i = 3; i < n + 1; i ++) { for (int j = 1; j < i;j ++) { dp[i] = Math.max(dp[i-j] * j, (i-j)*j); } } return dp[n]; } }
思路2:將繩子長度先儘量的劃分爲3的倍數,若是最後剩餘1,則拿出一個已經分配的3來湊成4。
例如:5 = 3 + 2
6 = 3 + 3 -> 3 * 3 = 9
7 = 3 + 3 + 1 = 3 + 4 -> 3 * 4 = 12
……數組
class Solution { public int cuttingRope(int n) { if (n <= 3) { return n - 1; } int remind = n; int multi = 1; while (remind > 4) { multi = multi * 3; remind -= 3; } return multi * remind; } }
題意:試題14- II. 剪繩子 II
思路:同剪繩子I優化
class Solution { public int cuttingRope(int n) { if (n <= 3) { return n - 1; } int remind = n; long multi = 1; while (remind > 4) { multi = (multi * 3) % 1000000007; remind -= 3; } return (int)((multi * remind) % 1000000007); } }
題意:面試題19. 正則表達式匹配
思路:用dp[i][j]表示s字符串前i個字符串是否與p的前j個字符串匹配,那麼所要求的結果就是dp[s.length()][p.length()]的值。
遞推式:對於某一個位置dp[i][j],是否匹配能夠分如下兩種狀況:
(1)當s[i] == p[j]時,若是s[0...i-1]與p[0...j-1]匹配,那麼s[0...i]與p[0...j]匹配,反之不匹配。即dp[i][j] = dp[i-1][j-1];
(2)當s[i] != p[j]時。若是p[j] != ‘*’,那麼s[0...i]與p[0...j]必定不匹配。若是p[j] == '*',表明p[j-1]位置的字符能夠出現0次或屢次
(a)當p[j-1]位置的字符不出現時,判斷s[0...i]與p[0...j-2]是否匹配便可,即dp[i][j] = dp[i][j-2]
(b)當p[j-1]位置的字符出現時,須要判斷s[i]位置上的字符是否與p[j-1] (注意能夠是'.')相同,相同則dp[i][j] = dp[i-1][j]ui
class Solution { public boolean isMatch(String s, String p) { int sLen = s.length(); int pLen = p.length(); char[] sArr = s.toCharArray(); char[] pArr = p.toCharArray(); boolean[][] dp = new boolean[sLen + 1][pLen + 1]; dp[0][0] = true; for (int i = 0; i < sLen + 1; i ++) { for (int j = 1; j < pLen + 1; j ++) { if (i > 0 && (sArr[i - 1] == pArr[j - 1] || pArr[j - 1] == '.')) { dp[i][j] = dp[i - 1][j - 1]; } else if (pArr[j - 1] == '*'){ if (i > 0 && j > 1 && (sArr[i - 1] == pArr[j - 2] || pArr[j - 2] == '.')) { dp[i][j] |= dp[i - 1][j]; } if (j > 1){ dp[i][j] |= dp[i][j - 2]; } } } } return dp[sLen][pLen]; } }
題意:面試題42. 連續子數組的最大和
思路:貪心。狀態轉移方程式爲sum[i] = max(sum[i-1] + nums[i], nums[i]),其中sum[i]表示元素nums[0...i]的和。spa
class Solution { public int maxSubArray(int[] nums) { int max = Integer.MIN_VALUE; int sum = 0; for (int i = 0; i < nums.length; i ++) { sum = Math.max(sum + nums[i], nums[i]); max = Math.max(max, sum); } return max; } }
題意:面試題47. 禮物的最大價值
思路:動態規劃。遞推公式dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j],優化爲一維後,第i行禮物最大價值:dp[j] = max(dp[j-1], dp[j]) + grid[i][j]。指針
class Solution { public int maxValue(int[][] grid) { int[] dp = new int[grid[0].length]; dp[0] = grid[0][0]; for (int j = 1; j < dp.length; j ++) { dp[j] = dp[j - 1] + grid[0][j]; } for (int i = 1; i < grid.length; i ++) { for (int j = 0; j < grid[0].length; j ++) { if (j == 0) { dp[j] += grid[i][j]; } else { dp[j] = Math.max(dp[j - 1], dp[j]) + grid[i][j]; } } } return dp[grid[0].length - 1]; } }
題意:面試題49. 醜數
思路:動態規劃。醜數是2,3,5的倍數,下一個醜數必定是前面某一個數乘以2或3或5獲得的最小數字。使用三個指針two、three、five分別指向二、三、5的倍數,下一個更大的數將由這三個指針指向的數字分別乘二、乘三、乘5取最小值獲得。取得最小值以後指針向前走一步。code
class Solution { public int nthUglyNumber(int n) { int two = 0; int three = 0; int five = 0; int[] dp = new int[n]; dp[0] = 1; for (int i = 1; i < n; i ++) { dp[i] = Math.min(Math.min(dp[two] * 2, dp[three] * 3), dp[five] * 5); if (dp[i] == dp[two] * 2) { two ++; } if (dp[i] == dp[three] * 3) { three ++; } if (dp[i] == dp[five] * 5) { five ++; } } return dp[n - 1]; } }
題意:面試題60. n個骰子的點數
思路:用freq[i][j]表示i個骰子,投出點數爲j的次數。那麼遞推式爲:
freq[i][j] = freq[i-1][j-1] + freq[i-1][j-2] + …… + freq[i-1][j-k](其中k<=6 且j-k>0,由於一個骰子只能投出1~6點)遞歸
class Solution { public double[] twoSum(int n) { int[][] freq = new int[n + 1][n * 6 + 1]; for (int i = 1; i <= 6; i ++) { freq[1][i] = 1; } int k; for (int i = 2; i <= n; i ++) { for (int j = i; j <= n * 6; j ++) { k = 1; while (k <= 6 && j - k > 0) { freq[i][j] += freq[i-1][j - k]; k ++; } } } double sum = Math.pow(6, n); double[] res = new double[5 * n + 1]; for (int i = 0; i < res.length; i ++) { res[i] = freq[n][n + i] / sum; } return res; } }
題意:面試題63. 股票的最大利潤
思路:動態規劃。遍歷數組的過程當中,記錄數組中最小的數字做爲股票買入的價格。當前遍歷到的數字減去最小的數字就是最大的利潤。
class Solution { public int maxProfit(int[] prices) { int max = 0; int min = Integer.MAX_VALUE; for (int i = 0; i < prices.length; i ++) { min = Math.min(min, prices[i]); max = Math.max(max, prices[i] - min); } return max; } }