题目:
Given an array with n
integers, your task is to check if it could become non-decreasing by modifying at most 1
element.
We define an array is non-decreasing if array[i] <= array[i + 1]
holds for every i
(1 <= i < n).
Example 1:
Input: [4,2,3]
Output: True
Explanation: You could modify the first 4 to 1 to get a non-decreasing array.
Example 2:
Input: [4,2,1]
Output: False
Explanation: You can't get a non-decreasing array by modify at most one element.
Note: The n
belongs to [1, 10,000].
题目很容易理解,就是只能动一个数字,将数列调整成非递减数列,非递减的定义也给出了,就是前一个不能大于后一个大,换句话说,后一个大于等于前一个。好了,题目理解完,来解题。
先来说一个暴利解法,暴力解法是就是数列一个数字一个数字来替换,看看换一个数字,数列是不是非递减了,唯一一个问题,要是换一个数字,换成什么数字比较合适呢?比如2,4,3,换2的时候换成谁呢?换4的时候换成谁?其实很简单,答案就是换成前一个数字就行了,就是说2换成一个负无穷,因为是2是第一个,前一个没有,4的话换成2,这样的话这组数列在替换的地方肯定是非递减了。数组变成2,2,3,再来看是不是非递减,一看非递减了,那么说明换一个数字就够了,返回真。
python 实现:
class Solution(object):
def checkPossibility(self, A):
def monotone_increasing(arr):
for i in range(len(arr) - 1):
if arr[i] > arr[i+1]:
return False
return True
new = A[:]
for i in xrange(len(A)):
old_ai = A[i]
new[i] = new[i-1] if i > 0 else float('-inf')
if monotone_increasing(new):
return True
new[i] = old_ai
return False
简单解释一下,先设定一个单调递增方法,这个方法只干一件事,就是把传进来的数组挨个比较,一旦发现前一个比后一个大,立马返回False. 否则就是True。 再来看主要步骤,先定义一个新的数组叫new, 开始循环,就是为了一个一个去替代,因为要换走一个数字,原始数字要靠一个临时变量来保存,这里用old_ai来保存原始数字。关键步骤来了,把当前数组的数字替换成前一个,如果是第一个,给一个负无穷。然后把这个新的数组传到单调递增方法检测,如果检测下来单调了,那么返回True, 如果不是,那么把临时变量放回这个数组,换下一个数字看看。直到所有数字都被替换过了,那么说明换一个肯定不够。时间复杂度很明显是O(N2),因为下面要换N个数字,上面方法又要检测N个数字。
这个暴力算法不够strightforward,不是一个常规思路。常规的话我们弄一个count,看到前一个大于后一个,count 加1,一旦大于1了,自然就是false了。但是这里有一个问题,就是有没有可能,你检测前后,count 只有一次,但是数组其他地方不是单调递增的呢?
解释一下:
count=0
for i in range(len(A)-1):
if A[i]>A[i+1]:
count+=1
return count<=1
这个显而易见的方法是错误的,因为下面这个反例:
[2,3,1,2]
如果单个看,只有3比1大, count 只被计数到了一次,但是实际来看,你换3,[2,1,1,2],不行,换 1, [2,3,2,2],不行,你换一个不会单调递增的,但是count只有一次,显而易见的暴力算法是错误的,这就是这道题tricky 的地方。
但是没关系,既然发现这个tricky的地方,我们就来解决他:看code:
class Solution:
def checkPossibility(self, nums: List[int]) -> bool:
count=0
for i in range(len(nums)-1):
if nums[i]>nums[i+1]:
count+=1
if count>1 or ((i>=1 and nums[i-1]>nums[i+1]) and (i+2nums[i+2])):
return False
return True
解释一下:设置一个count, 开始循环,碰到前一个比后一个大的,count +1, 都很strightforward, 好了,当count>1,那么结束了,解决count 是1 但是不是单调的情况,跟上面那个反例一样,
1)
((i>=1 and nums[i-1]>nums[i+1])
首先在第二个数字开始,你当前数字前一个不能大于当前数字后一个
然后:
2)
(i+2nums[i+2])):
在后面还有2个数字的情况下,你当前数字不能比后面第二个大。还是太抽象,举几个例子吧:
[2,3,1,2] 这种情况下,你怎么调都没用,假设现在到3,当前数字3,前一个2 大于 后一个 1,当前数字3又比后面第二个,2, 大,所以这是错误情况。
条件1和2,两者如果同时满足,数列就不可能靠换一个数字来单调递增。
如果只满足一个呢?
比如 [2,3,1,3],当前3,前一个2大于后一个1,但是第二条不满足,因为当前没有比后面第二个大,这种情况很显然把1换成3就是单调递增了。
再比如[2,3,2,2],当前3,后面第二个,2,的确比3小,但是第一条不满足,显然换3到2,[2,2,2,2]满足题目要求的。
所以,没有所以,代码在上,用一个strightforward的方法加corner case 解这道题