题目描述:
给定一个长度为 n
的整数数组,你的任务是判断在最多改变 1
个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i
(1 <= i < n),满足 array[i] <= array[i + 1]
。
示例 1:
输入: [4,2,3] 输出: True 解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
示例 2:
输入: [4,2,1] 输出: False 解释: 你不能在只改变一个元素的情况下将其变为非递减数列。
说明: n
的范围为 [1, 10,000]。
题干分析:
首先将逆序情况分类讨论:
CASE 1:【···2,3,2,4···】此时需修改第二个出现的【2】 为【3】或【4】,此部分即恢复有序;
CASE 2:【···2,3,4,1】||【9,1,2,3···】逆序点出现在头/尾,不难看出将其替换为相邻元素,此部分即可恢复有序;
解题思路:
题目给出的条件中有两点:①只允许修改一个数字;②修改后数列为非递减数列;
基于这两点需求,我们可以推演一下:符合条件的数组,以逆序点为分界线,分别构成了前、后两段非递减数组,并且在修改逆序点后,两段非递减数组会成功合并为一段非递减数组;如若不成立,此时数组不符合题目要求,return false;
那么我们的判定条件的优先级别便如下:
①两个指针(在数组中表达为下标序号即可,这里假设为a,b)分别从首尾出发,寻找第一个非有序位置;如果a,b两个指针相隔大于1,此时两段非递减数组不是相邻数组,直接返回false;
例如:
输入: [4,2,1]//此时a在0,b在2,直接判定false;
//首指针a寻找第一个nums[a]>nums[a+1]的位置,尾指针b寻找第一个nums[b]<nums[b-1]的位置;
②当a,b相邻或相等时,我们修改值的范围便是前段非递减数列的MAX与后段非递减数列的MIN;
即在【nums[a-1],nums[b+1]】的左右闭区间内,此时如果nums[a-1]>nums[b+1],那么取值区间为空,则不成立,return false;
③符合以上基本条件的同时,考虑处理特殊情况,在本题中特殊情况为:a==0||b==numsSize-1;此时为了保险起见,将a,b直接修改为INT_MIN/INT_MAX,如果修改后仍然存在逆序,即则return false;
例如:
输入: [3,2···]//此时a在0,将a修改为INT_MIN;
输入: [···3,2]//此时b在numsSize-1,将b修改为INT_MAX;
代码展示:终上所述,代码(一)如下:
bool checkPossibility(int* nums, int numsSize) {
int a= 0,b = numsSize - 1; //a,b首尾指针;
while(a< b && nums[a] <= nums[a + 1])
a++; //先从a开始判定,无论a走到哪里,b指针在前进时都不会越过a,给b的判定减少计算了;
while(a < b && nums[b] >= nums[b - 1])
b--; //题干要求非递减,故判定时使用>=;
int head = 0;
if(a == 0)
head = INT_MIN;
else
head = nums[a - 1]; //这里a的修改也可以写成b这种判定表达式,两种方式中a更直观,但更推荐b;
/* int head=(a==0)?INT_MIN:nums[a-1];*/
int next = (b==numsSize - 1)?INT_MAX:nums[b + 1];
if( b - a <= 1 && (head <= nums[b] || nums[a] <= next))
return true; //判定条件中第一条便是a,b的相对位置,如若不满足直接跳出判定,然后是区间判定,本数值区间是否有效,若无效则return false;
else
return false;
}
325 / 325 个通过测试用例
| 状态:通过 |
执行用时:
12 ms
|
如果想让代码更加简洁,可以如何处理?
bool checkPossibility(int* nums, int numsSize) {
int cnt = 1; //加入计数器cnt,如
int n = numsSize; int i;
for ( i = 1; i < n; ++i)
{
if (nums[i] < nums[i - 1])
{ if (cnt == 0)
return false;//计数器等于0时直接跳出;
if (i == 1 || nums[i] >= nums[i - 2])
nums[i - 1] = nums[i];
else nums[i] = nums[i - 1];
--cnt; //只要出现逆序计数器减一,第一次修改逆序数组,之后如果仍出现逆序,则直接返回false;
} //if
} //for
return true;
}//check
此做法唯一的问题在于修改了nums中的一个元素,在题目中我们固然可以这样做,但现实操作中这样可能得不偿失;
后续将每日日更新leetcode相关的基础算法题目,有兴趣的可以关注下,谢谢。