该题目标是在一个整数数组 nums 中寻找最长的“交替子数组”。这种交替子数组的特点是:其元素按照“递增1,递减1,递增1…”的模式循环排列,且子数组的长度必须大于1,例如数组nums = [2,3,4,3,4],交替子数组有 [2,3],[3,4],[3,4,3] 和 [3,4,3,4]。最长的子数组为 [3,4,3,4],长度为 4。详细题目描述见原题:原题。
这道题有一个重要的点是如何实现递增一、递减一、递增一的规律。观察发现,递增递减会导致相邻的两个数是相差1,相隔的两个数是相等,即索引差值为偶数时,两元素相等,索引差值为奇数时,两元素相差1,因此可以从这个角度去实现,判断子数组是否满足题目要求。
而对于符合条件的子数组,再判断是否为最长子数组,就需要不断更新当前子数组的长度,取更长的长度存入更新变量中。
因此,本体可以利用双层循环的方法,外层循环从左至右遍历数组,依次取每一个元素作为子数组的起始点,内层循环从起始点出发往右取子数组,并判断子数组是否满足条件。
int alternatingSubarray(int* nums, int numsSize) {
int res = -1;
for (int firstIndex = 0; firstIndex < numsSize; firstIndex++) {
for (int i = firstIndex + 1; i < numsSize; i++) {
int length = i - firstIndex + 1;
if (nums[i] - nums[firstIndex] == (length - 1) % 2) {
res = fmax(res, length);
} else {
break;
}
}
}
return res;
}
nums[i] - nums[firstIndex] == (length - 1) % 2;
res = fmax(res, length);
可以举例解释,如nums[1, 2, 1, 3, 1, 2],从上面双层循环的方法可知,当我们内层循环遍历到不符合条件的索引 i 时,会跳回到外层循环,此时符合条件的子数组是从nums[firstindex, …, i - 1],并将外层循环从firstindex右移到firstindex+1处进行后续判断。
但是,题目要求符合条件的子数组开始时必须是递增1。即nums[firstindex]到nums[firstindex+1]是递增1,nums[firstindex+1]到nums[firstindex+2]是递减1。
此时不难看出:
如果我们跳回到外层循环后还去从firstindex+1处遍历的话,那他到firstindex+2(如果存在的话)肯定是递减1,已经不符合开始时必须递增1的题目条件,属于是无效计算,浪费计算资源。
同理,如果子数组足够长,我们可以将外层循环从firstindex右移到firstindex+2处进行内层循环判断,此时firstindex+2到firstindex+3处是递增1,符合题目条件。然而,此时内层循环遍历到 i 时依然会break,所以此时的子数组长度也就是nums[firstindex+2, …, i-1],明显小于最初的nums[firstindex, …, i - 1]的长度,不可能是最长交替子数组。所以,从firstindex+2处开始往右遍历也是属于无效计算,浪费计算资源。
因此,当我们在nums[i]处发现不符合题目对交替子数组的要求时,下一步直接从nums[i-1]处进行处理,跳过前面那些nums[firstindex+1, firstindex+2, …i - 2],以节省计算资源。
这样做的话就不再需要外层循环的遍历,只保留内层循环。当子数组在nums[i]处被打破时,我们直接判断nums[i-1]是否可以作为新的符合条件的子数组的起点:
int alternatingSubarray(int* nums, int numsSize){
int res = -1;
int firstindex = 0;
for(int i = 1; i < numsSize; i++){
int length = i - firstindex + 1;
if(nums[i] - nums[firstindex] == (length - 1) % 2){
res = fmax(res, length);
}else{
if(nums[i] - nums[i - 1] == 1){
firstindex = i - 1;
res = fmax(res, 2);
}else{
firstindex = i;
}
}
}
return res;
}
if(nums[i] - nums[firstindex] == (length - 1) % 2){
res = fmax(res, length);
else{
if(nums[i] - nums[i - 1] == 1){
firstindex = i - 1;
res = fmax(res, 2);
}else{
firstindex = i;
}
这道题关键是通过什么方法来实现先递增再递减,只要理清了这个问题就很简单,除了本文提到的方法还可以使用双指针、动态规划和滑动窗口等方法解答。
分割线-------------------------------------------------------分割线
更新双指针解法
int alternatingSubarray(int* nums, int numsSize){
int res = 0;
for(int L = 0; L < numsSize - 1; L++) {
int R = L + 1; int pre = nums[R] - nums[L];
if(pre != 1) continue;
while(R + 1 < numsSize && nums[R + 1] - nums[R] == -pre) {
pre = -pre;
R++;
}
res = fmax(res, R - L + 1);
L = R - 1;
}
return res == 0 ? -1 : res;
}