先给题
给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
1.暴力破解这个就不谈了,保证第i个值小于等于后面的所有元素,如果有不符合的,数量不超过1就行。
2.根据条件判断我们要避免逐个进行比较,现在我们来讨论如何避免。
我们先不把那一个改变1个元素考虑在内,我们遇到的数列要么符合非递减数列,要么不符合。符合的自然不用说了,那么不符合的我们肯定会遇到至少一个 a b(a b 代之两个元素)b<a。
我们遇到第一个a b 就分情况判断能否改变 a 或者 b 中的一个值 来让它符合非递减数列,如果不能 那么就返回0,如果可以,那么接下来就不能再出现a b(a b 代之两个元素)b<a这样的情况。如果出现了,不需要讨论,直接返回0即可。
所以我们要在遇到第一个a b 的时候进行一下讨论,a和b要改变哪一个元素的数值,是由周围元素决定的。
我们知道 a>b 那么如果b后面没有元素,只需要让b增大即可。即此时 i == nums.size()-1 直接返回1即可。
如果b后面有元素c的情况呢,我们又要分两种情况c>=a 和 c <a。
c>=a 那么我们只需要让b变大就可以成为一个非递减数列,结束第一轮寻找a b,开启第二轮寻找a b。
c < a 的话 我们又要分两种情况 c < b 和c >=b。
c < b 则 a b c 是递减的,怎么更换也无法达成条件 返回0.
c >=b 那么我们要降低a的值就可以达成条件,而a的值必须<=b 即 a 可调的最大值是b,又分出两种情况 a前面有元素d和a前面没有元素d。
a前面没有元素d 即 a是第一个元素 a只要改变的值<=b就可以,是可以交换的,结束第一轮寻找a b,开启第二轮寻找a b。
a前面有元素d 如果想要达成条件,a可变的最大值一定要大于 d 所以又分了两种情况。
b >= d,是可以交换的,结束第一轮寻找a b,开启第二轮寻找a b。
b < d, a即便改变成b,也无法达成非递减数列的条件 返回0.
第二轮判断就简单了,只要遇到 a b这样的情况 直接返回0就可以了。
直接上代码了,注释我认为写的蛮清楚了。
1 bool checkPossibility(vector<int>& nums) { 2 int i = 1; 3 for (; i < nums.size(); i++) { 4 //当出现后者小于前者的情况时 即 a b 5 if(nums[i] < nums[i - 1]) { 6 //判断此时是不是最后一位,没有c 可以改变b,并且是最后一位 返回1 7 if(i == nums.size() - 1) 8 return 1; 9 //此时是 a b c这种情况 10 //c >= a 可以改变b 继续 11 else if (nums[i + 1] >= nums[i - 1]) 12 break; 13 //c < a 14 //c < b 连续三个数是递减的 不可能更换 15 else if (nums[i +1] < nums[i]) 16 return 0; 17 //c >= b 18 //a 前面没有d 可以改变a 19 else if (i == 1) 20 break; 21 //a 前面有d 判断是否可以改变a 22 //a可改变的值是a <= b a最大变为b 那么就判断b和d的关系 23 else if(nums[i] >= nums[i - 2]) 24 break; 25 //b < d 无法改变 26 else 27 return 0; 28 } 29 } 30 i++; 31 for (; i < nums.size(); i++) { 32 //第二次出现直接返回flase 33 if(nums[i] < nums[i - 1]) { 34 return 0; 35 } 36 } 37 return 1; 38 }
时间和内存通过是通过了,效率也不错,但是代码太复杂了,分的情况太多。那么接下来去看题解吧。
思考题解所说的是贪心算法
如果不符合题意的数列(不考虑更换),肯定会遇到a b,a >b。这和我开始的思想是一样的。
要么让a 变小,要么让 b 变大,那采取哪一种策略呢?我想的是去寻找周围元素,来判断它可不可以变,而我忽略了可以先让它变成适当的数,再去与周围的环境比较。(这样我们就没必要考虑c了,后续中肯定可以涉及到c)
这里就用到了贪心算法了,变化哪个影响是最小的呢,如果a可以变小的话那肯定是让a变小,因为前面已经确定了,a变小是不影响后面的判断的,如果a不能变小,我们也只能增大b,让b = a,然后继续让b与后续去比较。
只要a是第一个数,或者b的值>=a的前一个值,我们就可以让a 变小。
如果a不是第一个数且b的值<a,我们只能让b的值增大(我做的时候忽略了这两种条件其实包括了全部的情况)
修改后的代码
1 bool checkPossibility(vector<int>& nums) { 2 int flag = 1;//标记为可以交换 3 for (int i = 0; i < nums.size() - 1; i++) { 4 if (nums[i] > nums[i + 1]) { 5 if (!flag) 6 return 0; 7 flag = 0; 8 //开始交换 9 if (i == 0 || nums[i + 1] >= nums[i - 1]) 10 nums[i] = nums[i + 1]; 11 else 12 nums[i + 1] = nums[i]; 13 } 14 } 15 return 1; 16 }
https://leetcode-cn.com/problems/non-decreasing-array/solution/cyu-yan-tan-xin-si-xiang-by-liu-xiang-3-ia2u/
https://leetcode-cn.com/problems/non-decreasing-array/solution/yi-ding-yao-rang-ni-nong-dong-wei-shi-ya-u9te/
这是我参考的两个题解,如果还有不懂得可以去看一下,讲的很好。