Leet_code---665非递减数列---C语言版

题目描述:

给定一个长度为 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]

  ②当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;  
    }  

leetcode上的运行时间如下,有时会有差异;
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相关的基础算法题目,有兴趣的可以关注下,谢谢。




你可能感兴趣的:(Leet_code---665非递减数列---C语言版)