1. 523. 连续的子数组和
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
子数组大小 至少为 2 ,且子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。
示例 1:
输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
示例 2:
输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。
示例 3:
输入:nums = [23,2,6,4,7], k = 13
输出:false
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= sum(nums[i]) <= 231 - 1
1 <= k <= 231 - 1
涉及到的知识点:
1.前缀和:指的是数组中第i项(包括)以及其之前的所有的项之和。
2.K倍区间:指数组中某个区间[i,j]内的数之和是K的倍数,则这个区间成为K倍区间。
本题解题思路:就是利用一个公式:
a[0…i]=kXf1+b1
a[0…j]=kXf2+b2,那么a[0…j]-a[0…i]就是k的倍数,如果j-i>1那么就返回true!注意!!!,还有一种情况是没有余数相同但是直接a[0…i]%k == 0了,那么只要这个时候i>=1就可以,为了跟上面用一样的方法判断,这里直接map(0,-1)即可,用map去判断余数是否存在,更加方便,而且事实证明,O(n^2)不行。
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
if(nums.length<1){
return false;
}
Map<Integer,Integer> map=new HashMap<>();
map.putIfAbsent(0,-1);
map.putIfAbsent(nums[0]%k,0);
int sum=nums[0];
for(int i=1;i<nums.length;i++){
sum+=nums[i];
if(map.get(sum%k)!=null&&(i-map.get(sum%k))>1){
return true;
}
else{
map.putIfAbsent(sum%k,i);
}
}
return false;
}
}
2.剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
非常明显的一个动态规划题目:刚开始会纠结dp[i]表示什么,但是仔细分析会发现,假如dp[i]表示nums[0…i]中最大连续子数组最大和,那么dp[i+1]跟dp[i]的关系就没法出来,所以最终dp[i]表示nums[0…i]中包含nums[i]的连续子数组的最大和,dp[i]=max(dp[i-1]+nums[i],nums[i]),再求的过程中设置变量max保存dp最大值。
class Solution {
public int maxSubArray(int[] nums) {
int[] dp=new int[nums.length];
dp[0]=nums[0];
int max=dp[0];
for(int i=1;i<nums.length;i++){
if(dp[i-1]>=0){
dp[i]=dp[i-1]+nums[i];
}else{
dp[i]=nums[i];
}
if(max<dp[i]){
max=dp[i];
}
}
return max;
}
}
3. 面试题 08.01. 三步问题
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
示例1:
输入:n = 3
输出:4
说明: 有四种走法
示例2:
输入:n = 5
输出:13
提示:
n范围在[1, 1000000]之间
class Solution {
public int waysToStep(int n) {
//dp[i]=dp[i-1]+dp[i-3]+dp[i-2];
// int []dp=new int[n+1];
//dp[0]=1;
int a0=0,a1=0,a2=0,a3=0;
a0=1;
if(n==1){
return 1;
}
if(n==2){
return 2;
}
for(int i=1;i<=n;i++){
if(i==1){
a1=a0;
//dp[i]=dp[i-1];
}
else if(i==2){
a2=a1+a0;
//dp[i]=dp[i-1]+dp[i-2];
}else if(i>=3){
a3=((a0 + a1) % 1000000007 + a2) % 1000000007;
a0=a1;
a1=a2;
a2=a3;
//dp[i]=dp[i-1]+dp[i-2]+dp[i-3];
}
}
return a2;
}
}
4. 面试题 17.16. 按摩师
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
注意:本题相对原题稍作改动
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
示例 3:
输入: [2,1,4,5,3,1,1,3]
输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。
class Solution {
public int massage(int[] nums) {
//像小偷那一题。
//dp[i]表示前i个预约中最长时间预约集合。
//dp[i]=max(dp[i-2]+nums[i],dp[i-1])
//int[] dp=new int[nums.length+1];
if(nums.length==0){
return 0;
}
int a0,a1,a2;
a0=0;
a1=nums[0];
//dp[0]=0;
//dp[1]=nums[0];
for(int i=2;i<=nums.length;i++){
a2=Math.max(a0+nums[i-1],a1);
a0=a1;
a1=a2;
//dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]);
}
return a1;
}
}
5. 62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
实例1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109
dp[i][j]表示从0,0移动到i.j的路径数目。
dp[i][j]=dp[i-1][j]+dp[i][j-1];
dp[0][j]=dp[i][0]=1;
class Solution {
public int uniquePaths(int m, int n) {
//dp[i][j]表示从0,0移动到i.j的路径数目。
//dp[i][j]=dp[i-1][j]+dp[i][j-1];
//dp[0][j]=dp[i][0]=1;
int[][] dp=new int[m][n];
for(int i=0;i<m;i++){
dp[i][0]=1;
}
for(int j=0;j<n;j++){
dp[0][j]=1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}