Minimum Deletions to Make Array Divisible
HardPrompt
You are given two positive integer arrays nums and numsDivide. You can delete any number of elements from nums.
Return the minimum number of deletions such that the smallest element in nums divides all the elements of numsDivide. If this is not possible, return -1.
Note that an integer x divides y if y % x == 0.
Example 1:
Input: nums = [2,3,2,4,3], numsDivide = [9,6,9,3,15]
Output: 2
Explanation:
The smallest element in [2,3,2,4,3] is 2, which does not divide all the elements of numsDivide.
We use 2 deletions to delete the elements in nums that are equal to 2 which makes nums = [3,4,3].
The smallest element in [3,4,3] is 3, which divides all the elements of numsDivide.
It can be shown that 2 is the minimum number of deletions needed.Example 2:
Input: nums = [4,3,6], numsDivide = [8,2,6,10]
Output: -1
Explanation:
We want the smallest element in nums to divide all the elements of numsDivide.
There is no way to delete elements from nums to allow this.
Constraints:
1 <= nums.length, numsDivide.length <= 1051 <= nums[i], numsDivide[i] <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
This approach iterates through each unique candidate number from nums in ascending order. For each candidate, it checks if it divides every single element in numsDivide. The first candidate that satisfies this condition is the smallest possible one. The number of deletions would then be the count of elements in the original nums array that are smaller than this successful candidate.
Algorithm
- Sort the
numsarray in non-decreasing order. - Iterate through the sorted
numsarray with indexifrom0tonums.length - 1. - To avoid re-checking for duplicate values, if
i > 0andnums[i] == nums[i-1], continue to the next iteration. - For the current element
nums[i], check if it divides every element innumsDivide. - To do this, set a boolean flag
isDivisortotrue. Iterate throughnumsDivide. Ifd % nums[i] != 0for anydinnumsDivide, setisDivisortofalseand break the inner loop. - If after checking all elements of
numsDivide,isDivisoris stilltrue, it meansnums[i]is our target. Return the current indexi. - If the outer loop completes without returning, it means no element in
numscan divide all elements ofnumsDivide. Return -1.
Walkthrough
First, we sort the nums array to easily iterate through candidates in increasing order and to efficiently count deletions. We iterate through the sorted nums array. To avoid redundant checks for duplicate numbers, we skip a number if it's the same as the previous one. For each unique number num from nums, we assume it's our potential smallest element and check if it can divide all elements in numsDivide. We use a helper function or an inner loop for this check. If num divides all elements of numsDivide, we have found our target. Since we are iterating through the sorted nums array, this is the smallest such number. The number of deletions is its index i, which represents how many smaller elements came before it. We can return i. If we iterate through all of nums and no such number is found, it's impossible to satisfy the condition, so we return -1.
import java.util.Arrays; class Solution { public int minDeletions(int[] nums, int[] numsDivide) { Arrays.sort(nums); for (int i = 0; i < nums.length; i++) { // Skip duplicates to avoid redundant checks if (i > 0 && nums[i] == nums[i-1]) { continue; } int candidate = nums[i]; boolean dividesAll = true; for (int div : numsDivide) { if (div % candidate != 0) { dividesAll = false; break; } } if (dividesAll) { return i; } } return -1; }}Complexity
Time
O(N * M + N log N), where `N` is the length of `nums` and `M` is the length of `numsDivide`. Sorting `nums` takes `O(N log N)`. The nested loops can run up to `N * M` times in the worst case (if all elements in `nums` are unique). This is likely to cause a 'Time Limit Exceeded' error for large inputs.
Space
O(log N) or O(N), depending on the space used by the sorting algorithm. For an in-place sort like Heapsort it's O(1), for Quicksort it's O(log N) on average, and for Mergesort it's O(N).
Trade-offs
Pros
Conceptually simple and easy to understand.
It correctly solves the problem for smaller inputs.
Cons
Inefficient due to the nested loop structure. For each unique element in
nums, it iterates through the entirenumsDividearray, which can be very slow if both arrays are large.
A more efficient approach leverages a mathematical property: if a number x divides all elements in a set, it must also divide their greatest common divisor (GCD). Instead of checking divisibility against every element in numsDivide, we can compute their GCD once and check potential candidates from nums against this single GCD value.
Algorithm
- Define a helper function
gcd(a, b)that computes the greatest common divisor of two numbers using the Euclidean algorithm. - Calculate the GCD of all elements in
numsDivide. Initialize a variablegwithnumsDivide[0], then iterate through the rest ofnumsDivide, updatingg = gcd(g, numsDivide[i]). - Sort the
numsarray in non-decreasing order. - Iterate through the sorted
numsarray with indexi. - For each element
nums[i], check ifg % nums[i] == 0. - If the condition is met,
nums[i]is the smallest number innumsthat satisfies the property. The number of deletions isi. Returni. - If the loop finishes without finding such an element, return -1.
Walkthrough
The core idea is to find the smallest number in nums that divides the GCD of all elements in numsDivide. First, we calculate the GCD of all numbers in the numsDivide array. Let's call this g. The GCD can be computed iteratively: gcd(a, b, c) = gcd(gcd(a, b), c). If we find a number x in nums such that g % x == 0, then x is guaranteed to divide g. Since every number in numsDivide is a multiple of g, x will also divide every number in numsDivide. To find the minimum number of deletions, we need the smallest such x from nums. We sort the nums array in non-decreasing order. Then, we iterate through the sorted nums array. The first element nums[i] that divides g (i.e., g % nums[i] == 0) is our target. The number of deletions required to make nums[i] the smallest element is i, as all i elements before it are smaller and must be removed. If no element in nums divides g, it's impossible, so we return -1.
import java.util.Arrays; class Solution { private int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } public int minDeletions(int[] nums, int[] numsDivide) { // 1. Find GCD of all elements in numsDivide int g = numsDivide[0]; for (int i = 1; i < numsDivide.length; i++) { g = gcd(g, numsDivide[i]); } // 2. Sort nums to find the smallest valid number efficiently Arrays.sort(nums); // 3. Find the first number in sorted nums that divides the GCD for (int i = 0; i < nums.length; i++) { if (g % nums[i] == 0) { return i; // This is the number of deletions } } // 4. If no such number is found return -1; }}Complexity
Time
O(M * log(K) + N log N), where `N` is the length of `nums`, `M` is the length of `numsDivide`, and `K` is the maximum value in `numsDivide`. `O(M * log(K))` is for calculating the GCD of `numsDivide`. `O(N log N)` is for sorting `nums`. The final scan is `O(N)`. This is efficient enough to pass within the given constraints.
Space
O(log N) or O(N) for sorting, depending on the implementation. The GCD calculation is `O(1)` extra space.
Trade-offs
Pros
Highly efficient. The expensive check against all
Melements ofnumsDivideis replaced by a single check against their GCD.Reduces the time complexity significantly, making it feasible for large inputs.
Cons
Requires understanding the properties of the Greatest Common Divisor (GCD).
Solutions
Solution
class Solution {public int minOperations(int[] nums, int[] numsDivide) { int x = 0; for (int v : numsDivide) { x = gcd(x, v); } Arrays.sort(nums); for (int i = 0; i < nums.length; ++i) { if (x % nums[i] == 0) { return i; } } return -1; }private int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }}class Solution: def minOperations(self, nums: List[int], numsDivide: List[int]) -> int: x = numsDivide[0] for v in numsDivide[1:]: x = gcd(x, v) nums . sort() for i, v in enumerate(nums): if x % v == 0: return i return - 1class Solution {public: int minOperations(vector<int> &nums, vector<int> &numsDivide) { int x = 0; for (int &v : numsDivide) { x = gcd(x, v); } sort(nums.begin(), nums.end()); for (int i = 0; i < nums.size(); ++i) { if (x % nums[i] == 0) { return i; } } return -1; }};Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.