Talentd
FleetCode
Make and follow sheets.Sign in

Minimum Division Operations to Make Array Non Decreasing solution and explanation

MediumArrayGreedyMathNumber Theory7 min read29.1% acceptanceUpdated 9 Apr 2026
Leetcode Weekly Contest 420 | 3326. Minimum Division Operations to Make Array Non Decreasing CodeFodCodeFod · 15:35 · 733 viewsWatch on YouTube
4 more videos on this problem
  1. Leetcode Weekly Contest 420 | 3326. Minimum Division Operations to Make Array Non Decreasing CodeFodCodeFod · 15:35 · 733 views
  2. 3326. Minimum Division Operations to Make Array Non Decreasing (Leetcode Medium)Programming Live with Larry · 14:40 · 408 views
  3. 3326. Minimum Division Operations to Make Array Non Decreasing | Leetcode Weekly contest 420Dev · 14:57 · 288 views
  4. Leetcode | Weekly Contest 420 | Minimum Division Operations to Make Array Non Decreasing | SolutionCode Wanderer · 14:05 · 76 views
  5. 3326. Minimum Division Operations to Make Array Non Decreasing || #Leetcode Today Contest Solution||Jignesh · 0:55 · 29 views

The problem

You are given an integer array nums.

Any positive divisor of a natural number x that is strictly less than x is called a proper divisor of x. For example, 2 is a proper divisor of 4, while 6 is not a proper divisor of 6.

You are allowed to perform an operation any number of times on nums, where in each operation you select any one element from nums and divide it by its greatest proper divisor.

Return the minimum number of operations required to make the array non-decreasing.

If it is not possible to make the array non-decreasing using any number of operations, return -1.

Example 1:

Input: nums = [25,7]

Output: 1

Explanation:

Using a single operation, 25 gets divided by 5 and nums becomes [5, 7].

Example 2:

Input: nums = [7,7,6]

Output: -1

Example 3:

Input: nums = [1,1,1,1]

Output: 0

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 106

Approach overview

Problem Overview: You are given an integer array and can repeatedly divide an element by one of its divisors. The goal is to perform the minimum number of division operations so the array becomes non‑decreasing. If it cannot be achieved, return -1.

Approach 1: Greedy Approach with Stack (O(n log V) time, O(n) space)

The array must satisfy nums[i] ≤ nums[i+1]. Work from right to left and maintain a stack (or effectively track the allowed upper bound for the current element). If the current number is already ≤ the next valid value, push it and continue. If it is larger, repeatedly divide it by one of its valid divisors to reduce it. The greedy insight: always reduce the number as little as necessary so it becomes ≤ the next element. Using number‑theory operations (such as reducing by a smallest prime factor or divisor) ensures the value decreases quickly while minimizing operations. The stack keeps previously validated elements so you always know the current upper limit. Each element may be reduced a few times but values shrink quickly, giving roughly O(n log V) time where V is the max value.

This approach fits well with problems combining array traversal and greedy decisions. You locally enforce the ordering constraint and never revisit earlier indices.

Approach 2: Backtracking Approach (Exponential worst case, O(n) recursion space)

Backtracking explores all possible division choices for each element. For a given index, generate all values reachable by dividing the number with its divisors, then recursively check if the remaining array can be made non‑decreasing. Track the minimum operations among valid paths. While this guarantees the optimal answer, the branching factor can grow quickly because a number may have many divisors and multiple sequences of divisions. The worst‑case time becomes exponential, making it impractical for large inputs.

This method mainly demonstrates the brute force search space and helps reason about why greedy pruning works. The recursion explores combinations driven by number theory properties of divisors.

Recommended for interviews: The greedy right‑to‑left reduction is the expected solution. It enforces the non‑decreasing constraint while minimizing operations locally. Backtracking shows the full search space, but interviewers typically look for the greedy insight that shrinking each violating element to the largest valid value keeps the operation count minimal and runs efficiently.

Approach 1: Approach 1: Greedy Approach with Stack

This approach leverages a greedy method using a stack to maintain the non-decreasing property of the array. The goal is to use as few operations as possible to correct any decrease in the sequence.

We traverse the array from left to right, using a stack to track the current non-decreasing sequence. If the current number in the array is smaller than the top of the stack, it indicates a decrease in the sequence. We then repeatedly divide the number at the top of the stack by its greatest proper divisor until it is less than or equal to the current number, counting each operation. This ensures we only perform operations when necessary to maintain the non-decreasing order.

Finally, we check whether the sequence after the operations is non-decreasing and return the total number of operations. If the sequence cannot be made non-decreasing, we return -1.

def minOperations(nums):
    def greatest_proper_divisor(x):
        import math
        for i in range(int(math.sqrt(x)), 0, -1):
            if x % i == 0 and i != x:
                return x // i
        return 1

    operations = 0
    stack = []
    for num in nums:
        while stack and stack[-1] > num:
            top = stack.pop()
            while top >= num:
                top = greatest_proper_divisor(top)
                operations += 1
            stack.append(top)
        stack.append(num)
    return operations if stack == sorted(stack) else -1
function minOperations(nums) {
    const greatestProperDivisor = (x) => {
        for (let i = Math.floor(Math.sqrt(x)); i > 0; i--) {
            if (x % i === 0 && i !== x) {
                return x / i;
            }
        }
        return 1;
    };

    let operations = 0;
    let stack = [];

    for (let num of nums) {
        while (stack.length && stack[stack.length - 1] > num) {
            let top = stack.pop();
            while (top >= num) {
                top = greatestProperDivisor(top);
                operations++;
            }
            stack.push(top);
        }
        stack.push(num);
    }

    return isNonDecreasing(stack) ? operations : -1;

    function isNonDecreasing(arr) {
        for (let i = 1; i < arr.length; i++) {
            if (arr[i - 1] > arr[i]) return false;
        }
        return true;
    }
}

The Python solution employs a helper function to find the greatest proper divisor of a number. We iterate through the nums list, and for each number, we ensure the stack remains non-decreasing by popping elements when necessary and replacing them after dividing by their divisors. The total number of operations needed or -1 if unsuccessful is then returned.

Complexity

Time complexity: O(n * sqrt(max(nums[i]))) due to the divisor calculation for each element.
Space complexity: O(n) for the stack usage.

Try this approach in the editor

Approach 2: Approach 2: Backtracking Approach

This approach involves a recursive backtracking method where each element is iteratively tested with all possible division operations, exploring different combinations to find the minimal operation count that results in a non-decreasing array.

This is more of an exploratory approach, which attempts all possible divides at each step to find the minimally required operations, but is left here for completeness.

import java.util.Arrays;

public class Solution {
    public int greatestProperDivisor(int x) {
        for (int i = (int)Math.sqrt(x); i > 0; --i) {
            if (x % i == 0 && i != x) {
                return x / i;
            }
        }
        return 1;
    }

    public boolean isNonDecreasing(int[] nums) {
        for (int i = 1; i < nums.length; i++) {
            if (nums[i - 1] > nums[i]) {
                return false;
            }
        }
        return true;
    }

    public int helper(int[] nums, int index, int currentOps) {
        if (index == nums.length) {
            return isNonDecreasing(nums) ? currentOps : Integer.MAX_VALUE;
        }

        int num = nums[index];
        int minOps = Integer.MAX_VALUE;

        while (num > 0) {
            nums[index] = num;
            minOps = Math.min(minOps, helper(nums, index + 1, currentOps));
            num = greatestProperDivisor(num);
            currentOps++;
        }

        return minOps;
    }

    public int minOperations(int[] nums) {
        if (isNonDecreasing(nums)) return 0;
        int ops = helper(nums, 0, 0);
        return ops == Integer.MAX_VALUE ? -1 : ops;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] nums = {25, 7};
        System.out.println(sol.minOperations(nums));
    }
}
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int greatestProperDivisor(int x) {
    for (int i = sqrt(x); i > 0; --i){
        if (x % i == 0 && i != x) {
            return x / i;
        }
    }
    return 1;
}

bool canBeNonDecreasing(vector<int>& nums) {
    for (int i = 1; i < nums.size(); ++i) {
        if (nums[i-1] > nums[i]) {
            return false;
        }
    }
    return true;
}

int helper(vector<int>& nums, int index, int currentOps) {
    if (index == nums.size()) {
        return canBeNonDecreasing(nums) ? currentOps : INT_MAX;
    }

    int num = nums[index];
    int minOps = INT_MAX;

    while(num > 0) {
        nums[index] = num;
        minOps = min(minOps, helper(nums, index + 1, currentOps));
        num = greatestProperDivisor(num);
        ++currentOps;
    }

    return minOps;
}

int minOperations(vector<int>& nums) {
    if (canBeNonDecreasing(nums)) return 0;
    int ops = helper(nums, 0, 0);
    return ops == INT_MAX ? -1 : ops;
}

int main() {
    vector<int> nums = {25, 7};
    cout << minOperations(nums) << endl;
    return 0;
}

The Java solution mirrors the C++ backtracking approach, attempting all possible divisor combinations recursively. The program checks whether the modified array achieves a non-decreasing state, returning the minimum count of operations found or -1.

Complexity

Time complexity: Could be exponential in the worst-case scenario.
Space complexity: O(n), mainly due to recursion and backtracking stack requirements.

Try this approach in the editor

Approach 3: Preprocessing + Greedy

According to the problem description,

If an integer x is a prime number, then its largest proper divisor is 1, so x / 1 = x, meaning x cannot be divided further.

If an integer x is not a prime number, we assume the largest proper divisor of x is y, then x / y must be a prime number. Therefore, we find the smallest prime factor lpf[x] such that x bmod lpf[x] = 0, making x become lpf[x], at which point it cannot be divided further.

Thus, we can preprocess the smallest prime factor for each integer from 1 to 10^6. Then, we traverse the array from right to left. If the current element is greater than the next element, we change the current element to its smallest prime factor. If after changing the current element to its smallest prime factor it is still greater than the next element, it means the array cannot be made non-decreasing, and we return -1. Otherwise, we increment the operation count by one. Continue traversing until the entire array is processed.

The time complexity for preprocessing is O(M times log log M), where M = 10^6. The time complexity for traversing the array is O(n), where n is the length of the array. The space complexity is O(M).

const mx = 10 ** 6;
const lpf = Array(mx + 1).fill(0);
for (let i = 2; i <= mx; ++i) {
    for (let j = i; j <= mx; j += i) {
        if (lpf[j] === 0) {
            lpf[j] = i;
        }
    }
}

function minOperations(nums: number[]): number {
    let ans = 0;
    for (let i = nums.length - 2; ~i; --i) {
        if (nums[i] > nums[i + 1]) {
            nums[i] = lpf[nums[i]];
            if (nums[i] > nums[i + 1]) {
                return -1;
            }
            ++ans;
        }
    }
    return ans;
}
const mx int = 1e6

var lpf = [mx + 1]int{}

func init() {
	for i := 2; i <= mx; i++ {
		if lpf[i] == 0 {
			for j := i; j <= mx; j += i {
				if lpf[j] == 0 {
					lpf[j] = i
				}
			}
		}
	}
}

func minOperations(nums []int) (ans int) {
	for i := len(nums) - 2; i >= 0; i-- {
		if nums[i] > nums[i+1] {
			nums[i] = lpf[nums[i]]
			if nums[i] > nums[i+1] {
				return -1
			}
			ans++
		}
	}
	return
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Greedy Approach with Stack

Time complexity: O(n * sqrt(max(nums[i]))) due to the divisor calculation for each element.
Space complexity: O(n) for the stack usage.

Approach 2: Backtracking Approach

Time complexity: Could be exponential in the worst-case scenario.
Space complexity: O(n), mainly due to recursion and backtracking stack requirements.

Preprocessing + Greedy–

Complexity analysis

ApproachTimeSpaceWhen to Use
Greedy with StackO(n log V)O(n)Best general solution for large arrays where elements must be adjusted minimally
BacktrackingExponentialO(n)Useful for understanding all possible division paths or validating small inputs

Frequently asked questions

Is Minimum Division Operations to Make Array Non Decreasing easy or hard?

The problem is rated Medium on LeetCode with an acceptance rate around 29%. The difficulty comes from recognizing the greedy invariant and understanding how divisor reductions interact with the non‑decreasing constraint.

Minimum Division Operations to Make Array Non Decreasing Python/Java solution

Most implementations follow the same greedy idea. Traverse the array from right to left, compare each value with the next allowed value, and perform divisions when needed. This approach can be implemented in Python, JavaScript, Java, or C++ with roughly O(n log V) complexity.

How to solve Minimum Division Operations to Make Array Non Decreasing in O(n)?

A near‑linear strategy processes the array from right to left and keeps the maximum allowed value for the current position. When an element exceeds this bound, divide it by appropriate divisors until it becomes valid. Because each operation significantly reduces the number, the practical complexity behaves close to O(n) for typical constraints.

What is the best approach for Minimum Division Operations to Make Array Non Decreasing?

The greedy right‑to‑left reduction approach is the most efficient. Iterate from the end of the array and ensure each element is less than or equal to the next valid value. If it is larger, repeatedly divide it using valid divisors until the constraint holds. This method minimizes operations locally and runs in about O(n log V) time.

Is Minimum Division Operations to Make Array Non Decreasing asked at Google/Amazon/Meta?

Problems combining greedy logic with number‑theory style reductions appear in interviews at companies like Google, Amazon, and Meta. Variants often test the ability to enforce ordering constraints in arrays while minimizing operations.

What data structure is used in Minimum Division Operations to Make Array Non Decreasing?

The common implementation uses a stack or a running bound variable while iterating from right to left. The stack stores validated elements and helps maintain the maximum allowed value for the previous index. The core logic relies on array traversal and divisor calculations.

What is the time complexity of Minimum Division Operations to Make Array Non Decreasing?

The optimal greedy solution runs in O(n log V) time where n is the array length and V is the maximum element value. Each element may be reduced multiple times, but division quickly shrinks numbers. Space complexity is O(n) if a stack is used, or O(1) if only a running bound is tracked.

Comments

Now write it yourselfClose this page and solve Minimum Division Operations to Make Array Non Decreasing in the editor, without looking.
Practise Minimum Division Operations to Make Array Non Decreasing