This is part of a series of Leetcode solution explanations (index). If you liked this solution or found it useful, please like this post and/or upvote my solution post on Leetcode's forums.

Leetcode Problem #665 (Medium): Non-decreasing Array

Description:


(Jump to: Solution Idea || Code: JavaScript | Python | Java | C++)

Given an array nums with n integers, your task is to check if it could become non-decreasing by modifying at most one element.

We define an array is non-decreasing if nums[i] <= nums[i + 1] holds for every i (0-based) such that (0 <= i <= n - 2).

Examples:

Example 1:
Input: nums = [4,2,3]
Output: true
Explanation: You could modify the first 4 to 1 to get a non-decreasing array.
Example 2:
Input: nums = [4,2,1]
Output: false
Explanation: You can't get a non-decreasing array by modify at most one element.

Constraints:

  • n == nums.length
  • 1 <= n <= 10^4
  • -10^5 <= nums[i] <= 10^5

Idea:


(Jump to: Problem Description || Code: JavaScript | Python | Java | C++)

This problem seems easy at first read. If we iterate through the nums array (N), count the number of instances in which an element is lower than the one preceeding (err), and then see that count go above 1, then we should be able to return false. The problem becomes more difficult, however, once we realize that we're allowed to modify one element, which will naturally impact its relationship with the surrounding elements.

With that in mind, we can think of the different possible scenarios faced when we find an incidence of decreasing value. Consider the base scenario, with N[i-1] = a and N[i] = b:

Visual 1

From here, we can see that there are three possible scenarios for each of the surrounding elements (N[i-2] and N[i+1]). They can either be larger than a (x >= a), in between a and b (b < y < a) or smaller than b (z <= b):

Visual 2

Scenarios XAB and ABZ can quickly be determined to trigger a return of false because in both cases the err count will increment to 2:

Visual 3

Things get more complicated, however, once the values are staggered. In the case of ZABX, we either move a down or b up in order to achieve increasing values, while in YABX we can only move b up and in ZABY we can only move a down:

Visual 4

In the final scenario, YABY, there is no possible way to fix the array, even though there's only one incidence of a descending value:

Visual 5

With this all in mind, we can write our function to return false if we see err > 1 or if we see the YABY scenario. If we reach the end without triggering either condition, we can return true.

  • Time Complexity: O(N) where N is the length of N
  • Space Complexity: O(1) with no modification of inputs

Implementation:

There are only minor differences in the code of all four languages.

Javascript Code:


(Jump to: Problem Description || Solution Idea)

var checkPossibility = function(N) {
    for (let i = 1, err = 0; i < N.length; i++)
        if (N[i] < N[i-1])
            if (err++ || (i > 1 && i < N.length - 1 && N[i-2] > N[i] && N[i+1] < N[i-1]))
                return false 
    return true
};

Python Code:


(Jump to: Problem Description || Solution Idea)

class Solution:
    def checkPossibility(self, N: List[int]) -> bool:
        err = 0
        for i in range(1, len(N)):
            if N[i] < N[i-1]:
                if err or (i > 1 and i < len(N) - 1 and N[i-2] > N[i] and N[i+1] < N[i-1]):
                    return False
                err = 1
        return True

Java Code:


(Jump to: Problem Description || Solution Idea)

class Solution {
    public boolean checkPossibility(int[] N) {
        for (int i = 1, err = 0; i < N.length; i++)
            if (N[i] < N[i-1])
                if (err++ > 0 || (i > 1 && i < N.length - 1 && N[i-2] > N[i] && N[i+1] < N[i-1]))
                    return false;
        return true;
    }
}

C++ Code:


(Jump to: Problem Description || Solution Idea)

class Solution {
public:
    bool checkPossibility(vector<int>& N) {
        for (int i = 1, err = 0; i < N.size(); i++)
            if (N[i] < N[i-1])
                if (err++ || (i > 1 && i < N.size() - 1 && N[i-2] > N[i] && N[i+1] < N[i-1]))
                    return false;
        return true;
    }
};