目录
1.climbing-stairs
题目:你在爬楼梯,需要n步才能爬到楼梯顶部。每次你只能向上爬1步或者2步。有多少种方法可以爬到楼梯顶部?
分析:动态规划,dp[n] = dp[n-1] + dp[n-2]
public int climbStairs(int n) {
int x = 1,y = 1;
for(int i = 2;i <= n;i++){
int temp = x + y;
x = y;
y = temp;
}
return y;
}
2.triangle
题目:给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字。例如,给出的三角形如下:[↵ [2],↵ [3,4],↵ [6,5,7],↵ [4,1,8,3]↵],最小的从顶部到底部的路径和是2 + 3 + 5 + 1 = 11。注意:如果你能只用O(N)的额外的空间来完成这项工作的话,就可以得到附加分,其中N是三角形中的行总数。
分析:自下而上计算,由于只能移动到相邻位置,当前位置的最小值就是下层两个相邻数的最小值加上当前值。状态转移方程 :dp[i][j]= min(dp[i+1][j],dp[i+1][j+1]) + dp[i,j].
// [
// [2], [2],
// [3,4], [3, 4], [2],
// [6,5,7], ==> [7, 6, 10] ==> [9, 10] ==> [11]
// [4,1,8,3]
// ]
public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
if(triangle == null)
return 0;
int size = triangle.size();
int [] result = new int[size];
for(int i=0;i < size;i++)
result[i] = triangle.get(size-1).get(i);
for(int row = size-2;row >= 0;row--){
for(int j = 0;j <= row;j++){
result[j] = Math.min(result[j],result[j+1]) + triangle.get(row).get(j);
}
}
return result[0];
}
3.candy
题目:有N个小朋友站在一排,每个小朋友都有一个评分,你现在要按以下的规则给孩子们分糖果:每个小朋友至少要分得一颗糖果,分数高的小朋友要他比旁边得分低的小朋友分得的糖果多,你最少要分发多少颗糖果?
分析:先给每个小朋友都分一颗糖,从左到右扫描保证一个方向上分数高的糖更多,然后从右到左扫描保证另一个方向上分数高的糖更多,注意第二次分配时分数高的糖更多不再进行分配。
public int candy(int[] ratings) {
int len = ratings.length;
if(len < 2)
return len;
int[] candy = new int[len];
for(int i = 1;i < len;i++){//从左到右
if(ratings[i-1] < ratings[i])
candy[i] = candy[i-1] + 1;
}
int sum = len + candy[len - 1];
for(int i = len - 2;i >= 0;i--){//从右到左
if(ratings[i] > ratings[i+1] && candy[i] <= candy[i+1])
candy[i] = candy[i+1] + 1;
sum += candy[i];
}
return sum;
}
4.decode-ways
题目:一条仅包含字母‘A’-‘Z’的消息用下列的方式加密成数字'A' -> 1↵'B' -> 2↵...↵'Z' -> 26,现在给出加密成数字的密文,请判断有多少种解密的方法。例如:给出的密文为“12”,可以解密为"AB"(1 2) 或者"L"(12).所以密文"12"的解密方法是2种.
分析:动态规划。dp[i] 表示密文中前i个字符解密方法的种数,状态转移方程为:dp[i] = dp[i-1] + dp[i-2]。dp[i-1]表示加入单个字符(当前字符为‘0’时不能只加入单个字符),dp[i-2]表示当前字符与前一个字符凑(前面字符为1或2的时候才能凑)。易错:字符串不以0开头dp[1]才为1。
public int numDecodings(String s) {
if(s.length() == 0 || s.charAt(0) == '0')
return 0;
int[] dp = new int[s.length() + 1];
dp[0] = 1;dp[1] = 1;
for(int i = 2;i <= s.length();i++){
if(s.charAt(i-1) == '0')
dp[i] = 0;
else
dp[i] = dp[i-1];//当前字符不为0才能单独解密
if(s.charAt(i-2) == '1' || s.charAt(i-2) == '2' && s.charAt(i-1) <'7')
dp[i] += dp[i-2];
}
return dp[s.length()];
}
5.gray-code
题目:格雷码是一种二进制编码系统,如果任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code)。给定一个非负整数n,表示代码的位数,打印格雷码的序列。格雷码序列必须以0开头。例如:给定n=2,返回[0,1,3,2]
分析:当n = 0,格雷码为[0];当n = 1,格雷码为[0,1];当n = 2,格雷码为[00,01,11,10];当n = 3,格雷码为[000,001,011,010,110,111,101,100];后面每一个list的元素等于前面list每个元素前面加‘0’,和逆序遍历list每个元素前面加‘1’。
public ArrayList<Integer> grayCode(int n) {
ArrayList<Integer> result = new ArrayList<>();
if(n == 0){
result.add(0);
return result;
}
ArrayList<String> pre = new ArrayList<>();
ArrayList<String> cur;
pre.add("0");pre.add("1");
for(int i = 2;i <= n;i++){
cur = new ArrayList<>();
for(String s : pre)
cur.add("0" + s);
for(int j = pre.size() - 1;j >= 0;j--)
cur.add("1" + pre.get(j));
pre = cur;
}
for(String s : pre)
result.add(Integer.parseInt(s,2));
return result;
}
6.unique-paths
题目:一个机器人在m×n大小的地图的左上角(起点,下图中的标记“start"的位置)。机器人每次向下或向右移动。机器人要到达地图的右下角。(终点,下图中的标记“Finish"的位置)。可以有多少种不同的路径从起点走到终点?
上图是3×7大小的地图,有多少不同的路径?备注:m和n小于等于100
分析:动态规划。dp[m][n] = dp[m-1][n] + dp[m][n-1]
public int uniquePaths(int m, int n) {
int[][]dp = new int[m][n];
for(int i = 0;i < n;i++)
dp[0][i] = 1;
for(int i = 0;i < m;i++)
dp[i][0] = 1;
for(int row = 1;row < m;row++){
for(int col = 1;col < n;col++){
dp[row][col] = dp[row-1][col] + dp[row][col-1];
}
}
return dp[m-1][n-1];
}
7.unique-paths-ii
题目:继续思考题目"Unique Paths":如果在图中加入了一些障碍,有多少不同的路径?分别用0和1代表空区域和障碍。例如,下图表示有一个障碍在3*3的图中央。[↵ [0,0,0],↵ [0,1,0],↵ [0,0,0]↵]有2条不同的路径。备注:m和n不超过100.
分析:动态规划,在上题基础上考虑障碍的情况,在初始化和赋值多考虑一种情况即可。
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int row = obstacleGrid.length;
if(row == 0)
return 0;
int col = obstacleGrid[0].length;
int[][] dp = new int[row][col];
int i = 0;
while(i < col && obstacleGrid[0][i] == 0)
dp[0][i++] = 1;
while(i < col)
dp[0][i++] = 0;
i = 0;
while(i < row && obstacleGrid[i][0] == 0)
dp[i++][0] = 1;
while(i < row)
dp[i++][0] = 0;
for(i = 1;i < row;i++){
for(int j = 1;j < col;j++){
if(obstacleGrid[i][j] == 1)
dp[i][j] = 0;
else
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[row-1][col-1];
}
8.minimum-path-sum
题目:给定一个由非负整数填充的m x n的二维数组,现在要从二维数组的左上角走到右下角,请找出路径上的所有数字之和最小的路径。注意,你每次只能向下或向右移动。
分析:见剑指offer面试题47 https://blog.csdn.net/Nibaby9/article/details/10401824
9.subsets
题目:现在有一个没有重复元素的整数集合S,求S的所有子集。注意:你给出的子集中的元素必须按非递增的顺序排列;给出的解集中不能出现重复的元素。例如:如果S=[1,2,3], 给出的解集应为:[↵ [3],↵ [1],↵ [2],↵ [1,2,3],↵ [1,3],↵ [2,3],↵ [1,2],↵ []↵]
分析:将问题转化为集合S含k个元素的子集,注意空集也是子集的其中一个!!求集合中含固定元素的子集用回溯法即可。
public ArrayList<ArrayList<Integer>> subsets(int[] S) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if(S.length == 0)
return result;
ArrayList<Integer> list = new ArrayList<>();
Arrays.sort(S);
for(int k = 0;k <= S.length;k++)
util(S,result,0,k,list);//添加k个元素(注意空集也是子集),从下标0开始
return result;
}
private void util(int[] s, ArrayList<ArrayList<Integer>> result, int index, int k, ArrayList<Integer> list) {
if(k == 0){
result.add(new ArrayList<Integer>(list));
return;
}
for(int i = index;i < s.length;i++){
list.add(s[i]);
util(s,result,i + 1,k - 1,list);
list.remove(list.size() - 1);
}
}
10.subsets-ii
题目:给出一个可能包含重复元素的整数集合S,返回该整数集合的所有子集。注意:给出的子集中的元素要按非递增的顺序排列;给出的解集中不能包含重复的子集。例如:如果S =[1,2,2], 给出的解集应该是:[↵ [2],↵ [1],↵ [1,2,2],↵ [2,2],↵ [1,2],↵ []↵]
分析:只要在上题的基础上进行重复元素的处理即可。
public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] num) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if(num == null || num.length == 0)
return result;
ArrayList<Integer> list = new ArrayList<>();
Arrays.sort(num);
util(result,list,num,0);
return result;
}
private void util(ArrayList<ArrayList<Integer>> result, ArrayList<Integer> list, int[] num, int index) {
result.add(new ArrayList<>(list));
for(int i = index;i < num.length;i++){
if(i != index && num[i] == num[i-1])
continue;
list.add(num[i]);
util(result,list,num,i+1);
list.remove(list.size()-1);
}
}