Top81
//爬楼梯
//假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
//每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
1个的时候是1种,2个的时候是2种,
用递归的思想,假设n阶的时候是f(n),那么n-1阶是f(n-1),n-2阶是f(n-2),
n阶的最后一步,要么是爬1阶,也就是从n-1阶爬1阶上来,要么是爬2阶,也就是从n-2阶爬两阶上来,所以就有f(n) = f(n-1)+f(n-2);
f(1)=1; f(2) = 2;
所以第一可以用递归,但这个在验证的时候超时了,第二种方法就是建立一个长度为n的数组sum,第一个是1,第二个是2,其余的每一个都是它钱两个的和,返回sum[n-1]就可以了,空间复杂度是o(n)
第三个就是在第二个上面优化,直接使用常量遍历加就可以了
java
package TOP81_90;
/* 动态规划*/
//爬楼梯
//假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
//每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
public class Top81 {
public int climbStairs(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return climbStairs(n - 1) + climbStairs(n - 2);
}
public static int climbStairs2(int n) {
int[] sum = new int[n];
if (n >= 1) {
sum[0] = 1;
}
if (n >=2) {
sum[1] =2;
}
if(n>2){
for(int i = 2;i<n;i++){
sum[i] = sum[i-1]+sum[i-2];
}
}
return sum[n-1];
}
public static int climbStairs3(int n) {
int a = 1, b =2,sum =0;
if(n==1){
sum = a;
}
if(n ==2){
sum = b;
}
if(n>=3){
for(int i = 3;i<=n;i++ ){
sum = a+b;
a = b;
b = sum;
}
}
return sum;
}
public static void main(String[] args) {
System.out.println(climbStairs2(3));
System.out.println(climbStairs3(3));
}
}
Top82
杨辉三角
// 在「杨辉三角」中,每个数是它左上方和右上方的数的和。
//输入: numRows = 5
//输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
可以看到第一行和第二行 都是1以外
从第三行的数组开始,如果拿到前面一个数组的数据rown[],那么第n+1行数组的数据rown1
有rown1[0] = 1,row1[n] = 1;中间行数据rown1[i]=rown[i-1]+rown[i];所以也可以用递归
java
package TOP81_90;
import java.util.ArrayList;
import java.util.List;
//杨辉三角
//给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
// 在「杨辉三角」中,每个数是它左上方和右上方的数的和。
//输入: numRows = 5
//输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
public class Top82 {
public static List<List<Integer>> generate(int numRows) {
ArrayList<List<Integer>> lists = new ArrayList<>();
ArrayList<Integer> integers1 = new ArrayList<>();
//定义一个临时存储上一行结果的集合
ArrayList<Integer> temp = integers1;
for (int i = 1; i <= numRows; i++) {
temp = getRow(temp, i);
lists.add(temp);
}
return lists;
}
//该方法通过上一行的数据计算出本行的数据
public static ArrayList<Integer> getRow(List<Integer> list, int n) {
ArrayList<Integer> integers = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (i == 0 || i == n - 1) {
integers.add(1);
} else {
integers.add(list.get(i - 1) + list.get(i));
}
}
return integers;
}
public static void main(String[] args) {
System.out.println(generate(5));
}
}
打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你不触动警报装置的情况下,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
解析:这个参考了大神卡尔的解析思路
这部分转载自:https://leetcode.cn/problems/house-robber/
动态规划,
有 n 个房子,前 n 间能偷窃到的最高金额是 dp[n] ,前 n−1间能偷窃到的最高金额是 dp[n−1] ,此时向这些房子后加一间房,此房间价值为 num;
加一间房间后: 由于不能抢相邻的房子,意味着抢第 n+1 间就不能抢第 n 间;那么前 n+1间房能偷取到的最高金额 dp[n+1] 一定是以下两种情况的 较大值 :
不抢第 n+1 个房间,因此等于前 n 个房子的最高金额,即 dp[n+1]=dp[n];
抢第 n+1 个房间,此时不能抢第 n个房间;因此等于前 n−1个房子的最高金额加上当前房间价值,即 dp[n+1]=dp[n−1]+num;
细心的我们发现: 难道在前 nnn 间的最高金额 dp[n] 情况下,第 n 间一定被偷了吗?假设没有被偷,那 n+1 间的最大值应该也可能是 dp[n+1]=dp[n]+num 吧?其实这种假设的情况可以被省略,这是因为:
假设第 n 间没有被偷,那么此时 dp[n]=dp[n−1],此时 dp[n+1]=dp[n]+num=dp[n−1]+num,即两种情况可以 合并为一种情况 考虑;
假设第 n 间被偷,那么此时 dp[n+1]=dp[n]+num 不可取 ,因为偷了第 n间就不能偷第 n+1间。
最终的转移方程: dp[n+1]=max(dp[n],dp[n−1]+num);
java
package TOP81_90;
//打家劫舍(动态规划,结构化思路,清晰图解)
public class Top83 {
public static int rob(int[] moneys) {
int length = moneys.length;
int[] robs = new int[length];
robs[0] = moneys[0];
if (length > 1) {
robs[1] = Math.max(moneys[0], moneys[1]);
}
if (length > 2) {
for (int i = 2; i < length; i++) {
robs[i] = Math.max(robs[i - 2] + moneys[i], robs[i - 1]);
}
}
return robs[length - 1];
}
// 改成常量判断 空间复杂度降为o(1)
public static int rob2(int[] moneys) {
int pre = 0, cur = 0, tmp;
for(int num : moneys) {
tmp = cur;
cur = Math.max(pre + num, cur);
pre = tmp;
}
return cur;
}
public static void main(String[] args) {
int[] moneys = {1, 3, 5, 78, 4, 5, 6};
System.out.println(rob(moneys));
System.out.println(rob2(moneys));
}
}