文章目录
前言
动态规划相关题解
一、最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
class Solution {
public String longestPalindrome(String s) {
if(s==null||s.length()<2){
return s;
}
int start=0;
int end=0;
int len=1;
int n=s.length();
boolean[][] dp= new boolean[n][n];
for(int r=1;r<s.length();r++){
for(int l=0;l<r;l++){
if(s.charAt(l)==s.charAt(r)&&(dp[l+1][r-1]||(r-l)<=2)){
dp[l][r]=true;
if(r-l+1>len){
start=l;
end=r;
len=r-l+1;
}
}
}
}
return s.substring(start,end+1);
}
}
二、括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
有效括号组合需满足:左括号必须以正确的顺序闭合。
class Solution {
List<String> list=new ArrayList<String>();
public List<String> generateParenthesis(int n){
if(n<1){
return list;
}
dfs(0,0,"",n);
return list;
}
private void dfs(int left,int right,String s,int n){
if(left>n||right>n||right>left){
return;
}
if(s.length()==2*n){
list.add(s);
return;
}
dfs(left+1,right,s+"(",n);
dfs(left,right+1,s+")",n);
}
}
三、最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
class Solution {
public int maxSubArray(int[] nums){
if(nums.length==0){
return 0;
}
int dp[]=new int[nums.length];
dp[0]=nums[0];
int maxValue=dp[0];
for (int i = 0; i < nums.length; i++) {
dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
if(dp[i]>maxValue){
maxValue=dp[i];
}
}
return maxValue;
}
}
四、跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
class Solution {
public boolean canJump(int[] nums) {
int cover=0;
for (int i = 0; i <= cover; i++) {
cover=Math.max(cover,nums[i]+i);
if(cover>=nums.length-1){
return true;
}
}
return false;
}
}
五、不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp =new int[m][n];
for (int i = 0; i < m; i++) {
dp[i][0]=1;
}
for (int i = 0; i < n; i++) {
dp[0][i]=1;
}
for (int i = 1; i < m; i++) {
for (int i1 = 1; i1 < n; i1++) {
dp[i][i1]=dp[i-1][i1]+dp[i][i1-1];
}
}
return dp[m-1][n-1];
}
}
六、爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
class Solution {
public int climbStairs(int n) {
if(n==1){
return 1;
}
int[] dp=new int[n];
dp[0]=1;
dp[1]=2;
for (int i = 2; i < n; i++) {
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n-1];
}
}
七、解码方法
一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,“11106” 可以映射为:
“AAJF” ,将消息分组为 (1 1 10 6)
“KJF” ,将消息分组为 (11 10 6)
注意,消息不能分组为 (1 11 06) ,因为 “06” 不能映射为 “F” ,这是由于 “6” 和 “06” 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。
题目数据保证答案肯定是一个 32 位 的整数。
class Solution {
public int numDecodings(String s) {
if(s.charAt(0)==0){
return 0;
}
if(s.length()==1){
return 1;
}
int[] dp=new int[s.length()+1];
dp[0]=1;
for (int i = 1; i <= s.length(); i++) {
if(s.charAt(i-1)!='0'){
dp[i]=dp[i]+dp[i-1];
}
if(i>1&&s.charAt(i-2)!=0&&(s.charAt(i-2)-'0')*10+(s.charAt(i-1)-'0')<=26){
dp[i]+=dp[i-2];
}
}
return dp[s.length()];
}
八、杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> list = new ArrayList<>();
for (int i = 0; i < numRows; i++) {
List<Integer> integers = new ArrayList<>();
for (int i1 = 0; i1 <= i; i1++) {
if(i1==0||i1==i){
integers.add(1);
}else {
integers.add(list.get(i-1).get(i1-1)+list.get(i-1).get(i1));
}
}
list.add(integers);
}
return list;
}
}
九、买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
class Solution {
public int maxProfit(int[] prices) {
int[][] dp =new int[prices.length][2];
dp[0][0]=-prices[0];//初始余额
dp[0][1]=0;//初始利润
for (int i = 1; i < prices.length; i++) {
dp[i][0]=Math.max(dp[i-1][0],-prices[i]);
dp[i][1]=Math.max(dp[i-1][1],dp[i][0]+prices[i]);
}
return dp[prices.length-1][1];
}
}
//贪心
class Solution {
public int maxProfit(int[] prices) {
int min=Integer.MAX_VALUE;
int maxProfit=0;
for (int i = 0; i < prices.length; i++) {
if(prices[i]<min){
min=prices[i];
}
int profit=prices[i]-min;
if(profit>maxProfit){
maxProfit=profit;
}
}
return maxProfit;
}
}
十、买卖股票的最佳时机II
给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
class Solution {
public int maxProfit(int[] prices) {
int[][] dp =new int[prices.length][2];
dp[0][0]=-prices[0];//初始余额
dp[0][1]=0;//初始利润
for (int i = 1; i < prices.length; i++) {
dp[i][0]=Math.max(dp[i-1][0],dp[i-1][]-prices[i]);
dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return dp[prices.length-1][1];
}
}
//贪心
class Solution {
public int maxProfit(int[] prices) {
int maxpro=0;
int pro=0;
for (int i = 1; i < prices.length; i++) {
if(prices[i]>prices[i-1]){
pro=prices[i]-prices[i-1];
maxpro+=pro;
}
}
return maxpro;
}
}
十一、单词拆分
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int n=s.length();
boolean[] dp=new boolean[n+1];
dp[0]=true;
for (int i = 1; i <=n; i++) {
for (int j=0;j<i;j++){
if(dp[j]&&wordDict.contains(s.substring(j,i))){
dp[i]=true;
}
}
}
return dp[n];
}
}
十二、乘积最大数组
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
class Solution {
public int maxProduct(int[] nums){
if(nums.length==1){
return nums[0];
}
int max=Integer.MIN_VALUE;
int[][] dp=new int[nums.length][2];
dp[0][0]=nums[0];
dp[0][1]=nums[0];
for (int i = 1; i < nums.length; i++) {
if(nums[i]<0){
int tmp=dp[i-1][1];
dp[i-1][1]=dp[i-1][0];
dp[i-1][0]=tmp;
}
dp[i][0]=Math.max(dp[i-1][0]*nums[i],nums[i]);
dp[i][1]=Math.min((dp[i-1][1]*nums[i]),nums[i]);
max=Math.max(dp[i][0],max);
}
return Math.max(max,nums[0]);
}
}
十三、打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额
class Solution {
public int rob(int[] nums) {
if(nums.length==1){
return nums[0];
}
int[] dp =new int[nums.length];
dp[0]=nums[0];
dp[1]=Math.max(nums[1],dp[0]);
for(int i=2;i<nums.length;i++){
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.length-1];
}
}
十四、完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
class Solution {
public int numSquares(int n) {
int[] dp=new int[n+1];
Arrays.fill(dp,Integer.MAX_VALUE);
dp[0]=0;
for (int i = 0; i <= n; i++) {
for (int j = 1; j*j <=i ; j++) {
dp[i]=Math.min(dp[i],dp[i-j*j]+1);
}
}
return dp[n];
}
}
十五、最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
class Solution {
public int lengthOfLIS(int[] nums){
if(nums.length==1){
return 1;
}
int[] dp=new int[nums.length];//我们的定义是这样的:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度。
Arrays.fill(dp,1);
int max=0;
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j <i ; j++) {
if(nums[i]>nums[j]){ dp[i]=Math.max(dp[i],dp[j]+1);}
max=Math.max(max,dp[i]);
}
}
return max;
}
}
十六、零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
class Solution {
public int coinChange(int[] coins, int amount){
if(amount==0){
return 0;
}
int[] dp=new int[amount+1];//dp[j]:凑足总额为j所需钱币的最少个数为dp[j]
Arrays.fill(dp,amount+1);
dp[0]=0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j <coins.length ; j++) {
if(i>=coins[j]){dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);}
}
}
return dp[amount]>amount? -1:dp[amount];
}
}