目录
一.最小花费爬楼梯
1.题目
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
例子:
输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。
2.图解
3.代码
(1)时间复杂度为O(N),空间复杂度为O(N)
//使用最小花费爬楼梯
public int minCostClimbingStairs(int[] cost) {
//dp每个位置是cost当前位置的最小值,最后一个位置保存前面到顶部的最小值
int[] dp = new int[cost.length+1];
//相当于初始化可以从第一个或的第二个开始进行跳跃
for(int i=2; i<=cost.length; i++) {
dp[i] = Math.min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);//找前两个中的最小
}
return dp[cost.length];
}
(2)时间复杂度为O(N),空间复杂度为O(1)
public int minCostClimbingStairs2(int[] cost) {
//动态规划只与i-1和i-2有关,所以使用滑动数组
int pre = 0;//相当于保存i-2的值
int cur = 0;//相当于保存i-1的值
int n = cost.length;
for(int i=2; i<=n; i++) {
int next = Math.min(cur+cost[i-1], pre+cost[i-2]);//保存前面的最小值
pre = cur;
cur = next;
}
return cur;//cur就相当于最后cost.length的位置
}
二.买卖股票的最佳时机(1)
1.题目
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
例子:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2.图解
3.代码
public int maxProfit(int[] prices) {
int minValue = Integer.MAX_VALUE;//买进的最低价格
int maxPrice = 0;//卖出的最高价格
for(int i=0; i<prices.length; i++) {
if(minValue > prices[i]) {
//保存最低买进的价格
minValue = prices[i];
//判断是否满足当前的价钱是否和最低价格的差是否比最大利益大
}else if(prices[i]-minValue > maxPrice) {
maxPrice = prices[i] - minValue;
}
}
return maxPrice;
}
三.买卖股票的最佳时机(2)
1.题目
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
总利润为 4 。
2.思路图解
首先使用辅助二维数组,里面存储第i天的最大利润,然后将将买卖股票当天分为两种情况,
(1)一种是第i天不持有股票,这种又分为两种,一种是前一天不持有(i-1),一种是前一天持有,当天卖出。
所得的dp公式为dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+price[i]);
(2)另一种是第i天持有股票,再分为两种,一种是前一天持有,今天不卖,一种是前一天不持有,今天买入。
所得dp公式为:dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-price[i]);
(3)最终返回最后一天不买入的最大利润(因为最后一天买入,利润肯定减少)
3.代码
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
//dp[i][0] 表示第i天没持有股票的最大利润
//dp[i][1] 表示第i天持有股票的最大利润
dp[0][0] = 0;
dp[0][1] = 0-prices[0];
for(int i=1; i<prices.length; i++) {
//每种情况又分为两种
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+prices[i]);
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-prices[i]);
}
//最后一天不持有股票的最大利益大于比持有股票的最大利益
return dp[prices.length-1][0];
}
四.反转数位
1.题目
给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。
示例 1:
输入: num = 1775(11011101111)
输出: 8
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-bits-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2.图解
3.代码
public int reverseBits(int num) {
int cur = 0;//表示从前非零位置开始到当前位置1的个数
int insert = 0;//表示插入1个1的最长1的数位
int res = 0;
for(int i=0; i<32; i++) {
//说明第i位的二进制数非零
if((num&(1<<i))!= 0){
cur++;
insert++;
}else {
insert = cur+1;//第二次就会将之前插入的1的最长置为上一个开始0开始的位置的长度
cur = 0;//说明当前有0,cur重新开始
}
//取最大值
res = Math.max(res, insert);
}
return res;
}
五.最大正方形
1.题目
给定一个由'0'和'1'组成的2维矩阵,返回该矩阵中最大的由'1'组成的正方形的面积,输入的矩阵是字符形式而非数字形式。
示例:
输入:[[1,0,1,0,0],[1,0,1,1,1],[1,1,1,1,1],[1,0,0,1,0]]
返回值:4
2.图解
3.代码
public int solve (char[][] matrix) {
// 判断数组是否符合要求
if(matrix==null || matrix.length==0) {
return 0;
}
int row = matrix.length;
int col = matrix[0].length;
int[][] dp = new int[row][col];
int len = 0;
//由于数组下标从零开始,需要i-1参与运算,所以先确定第0行和第0列
for(int i=0; i<col; i++) {
if(matrix[0][i] == '1') {
dp[0][i] = 1;
}
}
for(int i=0; i<row; i++) {
if(matrix[i][0] == '1') {
dp[i][0] = 1;
}
}
//在dp数组中存放当前位置所能构成的正方形边长,然后与最大边长进行比较
for(int i=1; i<row; i++) {
for(int j=1; j<col; j++) {
if(matrix[i][j]== '1'){
dp[i][j] =
Math.min(dp[i-1][j],Math.min(dp[i-1][j-1],dp[i][j-1]))+1;
if(dp[i][j]>len) {
len = dp[i][j];
}
}
}
}
return len*len;
}
六.连续子数组的最大乘积
1.题目
输入一个长度为n的整型数组nums,数组中的一个或连续多个整数组成一个子数组。求所有子数组的乘积的最大值。
(1)子数组是连续的,且最小长度为1,最大长度为n
(2)长度为1的子数组,乘积视为其本身,比如[4]的乘积为4
(3)该题的数据保证最大的乘积不会超过int的范围,即不超过2^{32}-1232−1
示例:
输入:[3,2,-1,4]
返回值:6
说明:子数组[3,2]的乘积为6,[3,2,-1,4]的乘积为-24,[4]的乘积为4,故返回6
2.图解
3.代码
public int maxProduct (int[] nums) {
// write code here
int n = nums.length;
//使用dp数组保存当前元素存在的最大连续乘积
int[] maxDp = new int[n];
//使用minDp是因为存在负数,可能之前的最小负数和之后的负数乘积变为最大值
//minDp保存的是以当前元素结尾的最小连续子数组乘积
int[] minDp = new int[n];
maxDp[0] = nums[0];
minDp[0] = nums[0];
//保存连续数组的最大值乘积值
int res = nums[0];
//求前面连续数组乘积与当前元素乘积,当前元素中的最大最小值
for(int i=1; i<n; i++) {
maxDp[i] = Math.max(maxDp[i-1]*nums[i], Math.max(minDp[i-1]*nums[i], nums[i]));
minDp[i] = Math.min(maxDp[i-1]*nums[i], Math.min(minDp[i-1]*nums[i], nums[i]));
//maxDp【i】保存是以当前元素结尾的最大连续乘积
res = Math.max(res, maxDp[i]);
}
return res;
}
七.和为k的子数组
1.题目
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
输入:nums = [1,2,3], k = 3 结果为:(1,2) (3) 输出:2
2.图解
3.代码
public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
//首先在map中存放一个(0,1),这里代表的是从i=0到j的数组和满足条件
map.put(0,1);
//统计到当前位置的和
int sum = 0;
//最终结果
int res =0;
for(int i=0; i<nums.length; i++) {
sum += nums[i];
//sum指的是从0到i的数组和,而sum-k指的是当前位置到前面某一位置是否存在和为k的数组和
if(map.containsKey(sum-k)) {
//这里说明存在,就将出现的次数加起来
res += map.get(sum-k);
}
//如果到当前位置的数组和已经在之前存在,那么就将之前和的次数加1
//因为下一次计算到该位置,是从i到j的子数组和
map.put(sum, map.getOrDefault(sum,0)+1);
}
return res;
}
八.和为k的最长连续子数组的长度
1.题目
给定一个无序数组 arr , 其中元素可正、可负、可0。给定一个整数 k ,求 arr 所有连续子数组中累加和为k的最长连续子数组长度。保证至少存在一个合法的连续子数组。[1,2,3]的连续子数组有[1,2],[2,3],[1,2,3] ,但是[1,3]不是。
示例:
输入:[1,-2,1,1,0],0
和为0的数组有【1,-2,1】,【0】,最长的长度为3
返回值:3
2.图解
3.代码
public int maxlenEqualK (int[] arr, int k) {
// write code here
int sum = 0;
//res保存数组最大长度
int res = 0;
//map的键保存的是从0到i之间的是数组和,思路也是通过从i到j之间的连续数组和
Map<Integer,Integer> map = new HashMap<>();
//当从初始位置开始的时候,且数组下标总比长度少了一个,从-1开始,刚好是连续数组区间的长度
map.put(0, -1);
for(int i=0; i<arr.length; i++) {
sum += arr[i];
//保存从i到j之间是否存在指定的连续数组和的结果
int tmp = sum-k;
if(map.containsKey(tmp)) {
//每次找到一个满足要求的数组和,就找最大长度
res = Math.max(res, i-map.get(tmp));
}
if(!map.containsKey(sum)) {
//如果map中没有当前和,就添加
map.put(sum, i);
}
}
return res;
}