方法一(递归)
1.解题思路
- 递归终止条件:向左寻找时,寻找到索引0终止;向右寻找时,到索引nums.length-1终止。
- 递归如何推进:向左寻找时,如果大于左边元素,直接由左边索引子序列加一,否则重置为1;向右寻找时,如果小于右边元素,直接由右边索引子序列加一,否则重置为1。
- 每一层递归返回什么:当前层的最长子序列长度。
2.代码实现
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int一维数组
* @return int
*/
public int maxSubArrayLength (int[] nums) {
int n=nums.length;
if(n==1||n==2) return n;
//记录满足条件的连续子序列长度
int leftMax=nums[n-2]<100000?findLeft(nums,n-2)+1:findLeft(nums,n-2);
int rightMax=nums[1]>1?findRight(nums,1)+1:findRight(nums,1);
int res=Math.max(leftMax,rightMax);
//如果左边的最大值小于右边的最小值,那连起来就是递增的
for(int i=1;i<nums.length-1;i++){
if(nums[i-1]+1<nums[i+1]){
res=Math.max(res,findLeft(nums,i-1)+findRight(nums,i+1)+1);
}
else{
res=Math.max(res,Math.max(findLeft(nums,i-1)+1,findRight(nums,i+1)+1));
}
}
return res;
}
//从i索引开始寻找左边的最长连续上升子序列(包括i)
private int findLeft(int[] nums,int i){
if(i==0) return 1;
//如果大于左边元素,直接由左边索引子序列加一
if(nums[i]>nums[i-1]){
return findLeft(nums,i-1)+1;
}
return 1;
}
//从i索引开始寻找右边的最长连续上升子序列(包括i)
private int findRight(int[] nums,int i){
if(i==nums.length-1) return 1;
//如果小于右边元素,直接由右边索引子序列加一
if(nums[i]<nums[i+1]){
return findRight(nums,i+1)+1;
}
return 1;
}
}
3.复杂度分析
- 时间复杂度:在主循环体中,向左时,每次最多寻找i次,向右时,每次最多寻找(n-1-(i+1)+1)=n-i-1次,于是每次最多寻找(i+(n-i-1))=n-1次,总共有(n-2)*(n-1)次,所以时间复杂度是O(n2)。
- 空间复杂度:需要额外深度为n的递归栈,所以空间复杂度为O(n)。
方法二(动态规划)
1.解题思路
- 状态定义:left[i]表示索引i左边的最长子序列,right[i]表示索引i右边的最长子序列。
- 初始化:索引i在0处时,只有一个元素,left[0]为1,索引i在num.length-1处时,只有一个元素,right[num.length-1]为1。
- 状态转移:找左边的连续递增子序列时,如果大于左边的,在原来的基础上加一,否则重置为1;找右边的连续递增子序列时,如果小于右边的,在原来的基础上加一,否则重置为1。
动图展示:
2.代码实现
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int一维数组
* @return int
*/
public int maxSubArrayLength (int[] nums) {
// 初始化dp数组
int n=nums.length;
if(n==1||n==2) return n;
int[] left=new int[n];
int[] right=new int[n];
//找左边的连续递增子序列
left[0]=1;
for(int i=1;i<n;i++){
//如果大于左边的,在原来的基础上加一
if(nums[i]>nums[i-1]){
left[i]=left[i-1]+1;
}
//重置为1
else{
left[i]=1;
}
}
//找右边的连续递增子序列
right[n-1]=1;
for(int i=n-2;i>=0;i--){
//如果小于右边的,在原来的基础上加一
if(nums[i]<nums[i+1]){
right[i]=right[i+1]+1;
}
//重置为1
else{
right[i]=1;
}
}
int leftMax=nums[n-2]<100000?left[n-2]+1:left[n-2];
int rightMax=nums[1]>1?right[1]+1:right[1];
int res=Math.max(leftMax,rightMax);
//如果左边的最大值加1小于右边的最小值,那连起来就是递增的
for(int i=1;i<n-1;i++){
if(nums[i-1]+1<nums[i+1]){
res=Math.max(res,left[i-1]+right[i+1]+1);
}
else{
res=Math.max(res,Math.max(left[i-1]+1,right[i+1]+1));
}
}
return res;
}
}
3.复杂度分析
- 时间复杂度:三次遍历都是线性的,所以时间复杂度是O(n)。
- 空间复杂度:需要额外大小为n的left数组和right数组,所以空间复杂度为O(n)。