先给题

给你一个长度为 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就可以了。
直接上代码了,注释我认为写的蛮清楚了。

非递减数列(贪心)_数组非递减数列(贪心)_贪心算法_02
 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 }
View Code


时间和内存通过是通过了,效率也不错,但是代码太复杂了,分的情况太多。那么接下来去看题解吧。

3.按照题解

思考题解所说的是贪心算法

如果不符合题意的数列(不考虑更换),肯定会遇到a b,a >b。这和我开始的思想是一样的。
要么让a 变小,要么让 b 变大,那采取哪一种策略呢?我想的是去寻找周围元素,来判断它可不可以变,而我忽略了可以先让它变成适当的数,再去与周围的环境比较。(这样我们就没必要考虑c了,后续中肯定可以涉及到c)
这里就用到了贪心算法了,变化哪个影响是最小的呢,如果a可以变小的话那肯定是让a变小,因为前面已经确定了,a变小是不影响后面的判断的,如果a不能变小,我们也只能增大b,让b = a,然后继续让b与后续去比较。
只要a是第一个数,或者b的值>=a的前一个值,我们就可以让a 变小。
如果a不是第一个数且b的值<a,我们只能让b的值增大(我做的时候忽略了这两种条件其实包括了全部的情况)
修改后的代码

非递减数列(贪心)_数组非递减数列(贪心)_贪心算法_02
 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 }
View Code


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/
这是我参考的两个题解,如果还有不懂得可以去看一下,讲的很好。