文章目录
完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
class Solution {
/**
* 直接思路:找出N最接近的平方数,再循环找出剩余最接近的平方数集合(结果可能不是最优)
* 比如:12->9+1+1+1,最优的是12->4+4+4
* 所以,上面的思路还得把所有的情况都求出来,再选出最少的,性能较差
* <p>
* 优化思路:利用之前计算的步数,转换为动态规划方程
* dp[i]代表第i需要的最少步骤,遍历所有的情况,从而找出最优解
* for (int j = 1; i - j * j >= 0; j++) {
* dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
* }
*
*
* @param n
* @return
*/
public int numSquares(int n) {
//利用动态规划 定义长度为n+1的数组 对应索引所对应的数装最少的步数
int[] dp = new int[n + 1];
dp[0] = 0;
for (int i = 1; i <= n; i++) {
dp[i] = i; //先假设到这一步的最大的步数为每次+1
for (int j = 1; i - j * j >= 0; j++) { //i-j*j>=0 找到最大的j j*j就是i里面最大的完全平方数
//dp[i-j*j]+1 表示d[i-j*j]的步数+1 1即j*j这个完全平方数只需要一步
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
}
一条包含字母 A-Z 的消息通过以下方式进行了编码:
给定一个只包含数字的非空字符串,请计算解码方法的总数。
'A' -> 1
'B' -> 2
...
'Z' -> 26
输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。
class Solution {
public int numDecodings(String s) {
if (s.charAt(0) == '0') return 0;
int[] dp = new int[s.length() + 1];
dp[0] = dp[1] = 1;
for (int i = 2; i <= s.length(); i++) {
//如果该位不为'0',说明该位单独成字母合法
if (s.charAt(i - 1) != '0') {
dp[i] += dp[i - 1];
}
//如果后两位能组成"1x"(x为任意数字)或者"2x"(x小于7),说明最后两位组成字母合法
if ((s.charAt(i - 2) == '1') || (s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6')) {
dp[i] += dp[i - 2];
}
}
return dp[s.length()];
}
}
矩阵的总路径数
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
class Solution {
public int uniquePaths(int m, int n) {
int dp[][] = new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(i==0){
dp[i][j] = 1;
}else if(j==0){
dp[i][j] = 1;
}else{
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
}
//空间压缩
public int uniquePaths(int m, int n) {
int[] dp = new int[n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if(i == 0) dp[j] = 1;
else if(j != 0) dp[j] = dp[j] + dp[j - 1];
}
}
return dp[n - 1];
}
最小路径和
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
class Solution {
public int minPathSum(int[][] grid) {
if(grid.length == 0 || grid[0].length == 0) return 0;
int m = grid.length, n = grid[0].length;
int[] dp = new int[n];
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(j == 0) dp[0] = dp[0] + grid[i][0];
else if(i == 0) dp[j] = dp[j - 1] + grid[0][j];
else dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j];
}
}
return dp[n - 1];
}
/* public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int dp[][] = new int[m][n];
dp[0][0] = grid[0][0];
for(int i=1;i<m;i++){
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int j=1;j<n;j++){
dp[0][j] = dp[0][j-1] + grid[0][j];
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[m-1][n-1];
}*/
}
打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if(len==0){
return 0;
}
if(len==1){
return nums[0];
}
if(len==2){
return Math.max(nums[0],nums[1]);
}
int dp[] = new int[len+1];
dp[0] = 0;
dp[1] = nums[0];
for(int i=2;i<=len;i++){
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i-1]);
}
return dp[len];
}
}
最长上升子序列LeetCode
解题思路:
状态定义:
dp[i]dp[i] 的值代表 nums 前 ii 个数字的最长子序列长度。
转移方程: 设 j∈[0,i)j∈[0,i),考虑每轮计算新 dp[i]dp[i] 时,遍历 [0,i)[0,i) 列表区间,做以下判断:
当 nums[i] > nums[j]nums[i]>nums[j] 时: nums[i]nums[i] 可以接在 nums[j]nums[j] 之后(此题要求严格递增),此情况下最长上升子序列长度为 dp[j] + 1dp[j]+1 ;
当 nums[i] <= nums[j]nums[i]<=nums[j] 时: nums[i]nums[i] 无法接在 nums[j]nums[j] 之后,此情况上升子序列不成立,跳过。
上述所有 1. 情况 下计算出的 dp[j] + 1dp[j]+1 的最大值,为直到 ii 的最长上升子序列长度(即 dp[i]dp[i] )。实现方式为遍历 jj 时,每轮执行 dp[i] = max(dp[i], dp[j] + 1)dp[i]=max(dp[i],dp[j]+1)。
转移方程: dp[i] = max(dp[i], dp[j] + 1) for j in [0, i)。
初始状态:
dp[i]dp[i] 所有元素置 11,含义是每个元素都至少可以单独成为子序列,此时长度都为 11。
返回值:
返回 dpdp 列表最大值,即可得到全局最长上升子序列长度。
class Solution {
public int lengthOfLIS(int[] nums) {
int len = nums.length;
int dp[] = new int[len];
for(int i=0;i<len;i++){
dp[i] = 1;//初始化为1,以nums[i]为子序列的第一个元素
for(int j = 0;j<i;j++){
if(nums[j]<nums[i]){
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
}
int max = 0;
for(int i=0;i<len;i++){
max = Math.max(dp[i],max);
}
return max;
}
}
最长摆动序列LeetCode
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
class Solution {
public int wiggleMaxLength(int[] nums) {
int up = 1;
int down =1;
int len = nums.length;
if(len==0){
return 0;
}
for(int i=1;i<len;i++){
if(nums[i]>nums[i-1]){
up = down + 1;
}else if(nums[i]<nums[i-1]){
down = up + 1;
}
}
return Math.max(up,down);
}
}