53. 最大子数组和
public int maxSubArray(int[] nums) {
/**
以当前索引结尾的情况下,最大的累加和
由动态规划经过空间压缩后,用一个变量来更新记录
有两种情况:
1. pre<0的时候,当前数值nums[i]为最好情况
2.pre>0时,pre+nums[i]
*/
int ans=nums[0];
for(int i=1,pre=nums[0];i<nums.length;i++){
pre = Math.max(nums[i],pre+nums[i]);
ans = Math.max(ans,pre);
}
return ans;
}
198. 打家劫舍
public int rob(int[] nums) {
/**
要保证最大金额,且不相邻,可以从是否考虑当前元素的角度
则取决于nums[i],nums[i]+dp[i-2],nums[i]三种情况的最大值
空间压缩,则需要两个变量进行动态更新
*/
int n = nums.length;
// 数组长度为1时
if(n==1){
return nums[0];
}
// 数组长度为2时
if(n==2){
return Math.max(nums[0],nums[1]);
}
int prePre=nums[0];
int pre = Math.max(nums[0],nums[1]);
for(int i =2,cur;i<n;i++){
cur=Math.max(pre,Math.max(prePre+nums[i],nums[i]));
prePre=pre;
pre=cur;
}
return pre;
}
918. 环形子数组的最大和
public int maxSubarraySumCircular(int[] nums) {
/**
有两种情况,1.连续,那就是求子数组的最大累加和
2.不连续,位于数组的首尾部分,那就考虑用整个数组的和,减去中间连续的最小和
*/
int all=nums[0], maxPre=nums[0], minPre=nums[0];
int maxSum=nums[0], minSum=nums[0];
for(int i =1;i<nums.length;i++){
all+=nums[i];
maxPre = Math.max(nums[i],nums[i]+maxPre);
maxSum = Math.max(maxPre,maxSum);
minPre = Math.min(nums[i],nums[i]+minPre);
minSum = Math.min(minPre,minSum);
}
// 排除特殊情况,当数组总和等于最小和时,但要求不能返回空数组,那么就去maxSum,
// 即所有小数中最大的
return all==minSum ? maxSum : Math.max(maxSum,all-minSum);
}
213. 打家劫舍Ⅱ
public int rob(int[] nums) {
/**
这里可以从是否考虑nums[0]的角度着手
1.确定要nums[0],那么就是在[2...n-2]上进行不能选相邻元素的最大累加和
2.不要nums[0],那么就是在[1...n-1]上进行不能选相邻元素的最大累加和操作
*/
int n =nums.length;
// 数组长度为1时
if(n==1){
return nums[0];
}
// 数组长度为2时
if(n==2){
return Math.max(nums[0],nums[1]);
}
return Math.max(f(nums,1,n-1),nums[0]+f(nums,2,n-2));
}
public static int f(int[] nums, int l, int r){
// 当前范围无效
if(l>r){
return 0;
}
// 当前范围只要一个元素
if(l==r){
return nums[l];
}
// 当前范围只要两个元素
if(l+1==r){
return Math.max(nums[l],nums[r]);
}
int prePre = nums[l];
int pre = Math.max(nums[l],nums[l+1]);
for(int i=l+2, cur; i<=r;i++){
cur = Math.max(pre,Math.max(prePre+nums[i],nums[i]));
prePre=pre;
pre=cur;
}
return pre;
}
2560. 打家劫舍Ⅳ
public int minCapability(int[] nums, int k) {
/**
二分法+动态规划
首先通过二分法来确定最小的窃取能力
通过动态规划,在确定了最小能力的请款报告下,判断能偷几间房
由于这里统计的是几间房,而不是收益金额,可以小贪心优化一下
只要能投这个房间就偷,并且跳两下,保证不相邻
否则跳一下
*/
int n = nums.length,l=nums[0],r=nums[0];
for(int i=1;i<n;i++){
l=Math.min(l,nums[i]);
r=Math.max(r,nums[i]);
}
int m,ans=0;
while(l<=r){
m=(l+r)/2;
if(f(nums,n,m)>=k){
ans=m;
r=m-1;
}else{
l=m+1;
}
}
return ans;
}
public int f(int[] nums, int n, int ability){
int ans = 0;
for(int i =0; i<n;i++){
if(nums[i]<=ability){
ans++;
i++;
}
}
return ans;
}
面试题17.24. 最大子矩阵
public int[] getMaxMatrix(int[][] matrix) {
/**
时间复杂度为O(n^3)
将二维数组转换成一维数组
*/
int n=matrix.length;
int m=matrix[0].length;
int max = Integer.MIN_VALUE;
int a=0,b=0,c=0,d=0;
// 用一个一维数组来记录子矩阵的压缩值
int[] nums = new int[m];
for(int up=0;up<n;up++){
// 每一轮(即压缩的开始行不同)时需要清空
Arrays.fill(nums,0);
for(int down = up;down<n;down++){
// 接下来就是在一维子数组中找到最大的累加和
for(int l=0,r=0,pre = Integer.MIN_VALUE;r<m;r++){
// 将up-down行的数据累加压缩到一维数组中
nums[r]+=matrix[down][r];
// 当一维数组中的pre大于0时,就累加
// 否则,更新为nums[r],并且开始的索引更新为当前r
if(pre>=0){
pre+=nums[r];
}else{
pre=nums[r];
l=r;
}
// 当找到更大的值时,更新max,并更新左上角和右下角的索引编号a,b,c,d
if(pre>max){
max=pre;
a=up;
b=l;
c=down;
d=r;
}
}
}
}
return new int[]{a,b,c,d};
}
152. 乘积最大子数组
public int maxProduct(int[] nums) {
/**
对于当前元素有三种情况得到乘积最大
1. 当前元素本身(前一个元素为小数时)
2.当前元素与前一个最大乘积相乘
2.当前元素与前一个最小乘积相乘(当前元素为负数时)
*/
int ans =nums[0] ;
for(int i=1, max=nums[0],min=nums[0],curMax,curMin;i<nums.length;i++){
curMax=Math.max(nums[i],Math.max(nums[i]*max,nums[i]*min));
curMin=Math.min(nums[i],Math.min(nums[i]*max,nums[i]*min));
max=curMax;
min=curMin;
ans=Math.max(ans,max);
}
return ans;
}
689. 三个无重叠子数组的最大和
public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
/**
本题需要三个辅助参数
1.sums[i]表示以i开头的k长度的累加和
2.prefix[i]表示[0...i]范围上长度为k的子数组中最大sum的开头索引
3.suffix[i]:表示[i...n-1]范围上长度为k的子数组和最大的开索引
注意:> >=的地方
*/
int n = nums.length;
int[] sums = new int[n];
for(int l=0,r=0,sum=0;r<n;r++){
sum+=nums[r];
if(r-l+1==k){
sums[l]=sum;
sum-=nums[l];
l++;
}
}
// prefix[i]
int[] prefix=new int[n];
for(int l=1,r=k;r<n;r++,l++){
// 只能时> 保证字典序最小,相等也不更新
if(sums[l]>sums[prefix[r-1]]){
prefix[r]=l;
}else{
prefix[r]=prefix[r-1];
}
}
// suffix[i]
int[] suffix=new int[n];
suffix[n-k]=n-k;
for(int l=n-k-1;l>=0;l--){
// 为了保证字典序最小,在相等时必须更新
if(sums[l]>=sums[suffix[l+1]]){
suffix[l]=l;
}else{
suffix[l]=suffix[l+1];
}
}
int a=0,b=0,c=0,max=0;
for(int p,s,i=k,j=2*k-1,sum;j<n-k;i++,j++){
p=prefix[i-1];
s=suffix[j+1];
sum=sums[p]+sums[s]+sums[i];
if(sum>max){
max=sum;
a=p;
b=i;
c=s;
}
}
return new int[] {a,b,c};
}