從Triangle這個問題提及:java
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.面試
Example:算法
Given the following triangle:數組
[ide
[2],函數
[3,4],優化
[6,5,7],spa
[4,1,8,3]code
]blog
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
遇到這道題,老師說開始先給出暴力解法以後進行優化是能夠的,不必定一開始就給出最優解,想出來一個暴力解法以爲傻就不說話。。。
其中時間複雜度爲O(2^{n}), 其中n爲Triangle中的層數(122*2…)進去n次,每次調用2次本身,因此O(2^{n-1}),近似爲O(2^{n}), 注意使用Traverse的方法的時候,結果是看成函數參數傳遞的。
時間複雜度依然爲O(2^{n}), 其中n爲Triangle中的點數,同使用Traverse的方法不一樣的是,Divide and Conquer 的結果是做爲函數返回值而不是函數參數,要否則無法conquer。
–DFS:Divide and Conquer 加 memorization
時間複雜度爲O(n^{2}),其中n爲Triangle的層數。
其中注意,若是求最小值的時候,初始化每每是int或其類型的最大值;反之若是求最大值,初始化每每爲最小值
記憶法的分治時間複雜度計算:
a=一共多少個點
b=每一個點處理的時間複雜度
c=每一個點訪問幾回
分治法的總時間複雜度=a*b*c
–Traditional Dynamic Programming
// version 0: top-down public class Solution { /** * @param triangle: a list of lists of integers. * @return: An integer, minimum path sum. */ public int minimumTotal(int[][] triangle) { if (triangle == null || triangle.length == 0) { return -1; } if (triangle[0] == null || triangle[0].length == 0) { return -1; } // state: f[x][y] = minimum path value from 0,0 to x,y int n = triangle.length; int[][] f = new int[n][n]; // initialize f[0][0] = triangle[0][0]; for (int i = 1; i < n; i++) { f[i][0] = f[i - 1][0] + triangle[i][0]; f[i][i] = f[i - 1][i - 1] + triangle[i][i]; } // top down for (int i = 1; i < n; i++) { for (int j = 1; j < i; j++) { f[i][j] = Math.min(f[i - 1][j], f[i - 1][j - 1]) + triangle[i][j]; } } // answer int best = f[n - 1][0]; for (int i = 1; i < n; i++) { best = Math.min(best, f[n - 1][i]); } return best; } } //Version 1: Bottom-Up public class Solution { /** * @param triangle: a list of lists of integers. * @return: An integer, minimum path sum. */ public int minimumTotal(int[][] triangle) { if (triangle == null || triangle.length == 0) { return -1; } if (triangle[0] == null || triangle[0].length == 0) { return -1; } // state: f[x][y] = minimum path value from x,y to bottom int n = triangle.length; int[][] f = new int[n][n]; // initialize for (int i = 0; i < n; i++) { f[n - 1][i] = triangle[n - 1][i]; } // bottom up for (int i = n - 2; i >= 0; i--) { for (int j = 0; j <= i; j++) { f[i][j] = Math.min(f[i + 1][j], f[i + 1][j + 1]) + triangle[i][j]; } } // answer return f[0][0]; } }
此題中的時間複雜度的a爲O(n^{2}), b和c均爲O(1), 最後的結果爲O(n^{2})。
1.動態規劃是一種算法思想, 是高於算法的. 而分治的記憶化搜索是實現動態規劃的一種手段. 2.那麼什麼是動態規劃呢? -就感受上來講, 動態規劃的是"一層一層來", 基於前一個狀態推出如今的狀態. 3.動態規劃爲何會快呢? -由於減小了不少沒必要要的重複計算. 4.動態規劃和分治的區別? -動態規劃約等於分治+記憶化, 由於有了記憶化, 因此算過的直接用就行, 就不用再算一遍了. 5.動態規劃有方向性,不回頭,不繞圈兒,無循環依賴。
動態規劃適合把暴力時間複雜度爲指數型的問題轉化爲多項式的複雜度,即O(2^{n})或O(n!) 轉化爲O(n^{2})
1.求最大最小的問題
2.判斷可不可行,存不存在
3.統計方案個數
• 不擅長優化n^3到n^2
思惟模式一個正向,一個逆向
自底向上代碼以下:
時間複雜度: O(n^{2})
空間複雜度:O(n^{2}),看開了一個多大的數組就是結果,莫想的太複雜…
自頂向上代碼以下:
時間複雜度依舊: O(n^{2})
空間複雜度依舊:O(n^{2}),依舊看開了一個多大的數組就是結果,依舊莫想的太複雜…
1.狀態:即定義,中間的狀態 2.方程:即從前一種狀態推出如今的狀態. 3.初始化:極限小的狀態,即爲起點. 4.答案:終點
1. 定義(狀態) • 接受什麼參數 • 作了什麼事 • 返回什麼值 2. 拆解(方程) • 如何將參數變小 3. 出口(初始化) • 何時能夠直接 return
1.座標型15% 2.序列型30% 3.雙序列型30% 4.劃分型10% 5.揹包型10% 5.區間型5%
沒有左上和右上的提出來先算,也能夠說成二維DP問題, [i,0]和[0,i]先初始化,即初始化第0行和第0列.
A robot is located at the top-left corner of a m x n grid.
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid.
public class Solution { public int uniquePaths(int m, int n) { if (m == 0 || n == 0) { return 1; } int[][] sum = new int[m][n]; for (int i = 0; i < m; i++) { sum[i][0] = 1; } for (int i = 0; i < n; i++) { sum[0][i] = 1; } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { sum[i][j] = sum[i - 1][j] + sum[i][j - 1]; } } return sum[m - 1][n - 1]; } }
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
public class Solution { public int minPathSum(int[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return 0; } int M = grid.length; int N = grid[0].length; int[][] sum = new int[M][N]; sum[0][0] = grid[0][0]; for (int i = 1; i < M; i++) { sum[i][0] = sum[i - 1][0] + grid[i][0]; } for (int i = 1; i < N; i++) { sum[0][i] = sum[0][i - 1] + grid[0][i]; } for (int i = 1; i < M; i++) { for (int j = 1; j < N; j++) { sum[i][j] = Math.min(sum[i - 1][j], sum[i][j - 1]) + grid[i][j]; } } return sum[M - 1][N - 1]; } }
Given a sequence of integers, find the longest increasing subsequence (LIS).
You code should return the length of the LIS.
不要用貪心法, 你能想到的貪心法都是錯的
面試不會考貪心法
貪心法沒有通用性
起點不肯定,終點也不肯定,一個小人, 只能往高跳,最多踩多少個木樁:
public class Solution { /** * @param nums: The integer array * @return: The length of LIS (longest increasing subsequence) */ public int longestIncreasingSubsequence(int[] nums) { int []f = new int[nums.length]; int max = 0; for (int i = 0; i < nums.length; i++) { f[i] = 1; for (int j = 0; j < i; j++) { if (nums[j] f[j] + 1 ? f[i] : f[j] + 1; } } if (f[i] > max) { max = f[i]; } } return max; } }