Minimum Seconds to Equalize a Circular Array
MedPrompt
You are given a 0-indexed array nums containing n integers.
At each second, you perform the following operation on the array:
- For every index
iin the range[0, n - 1], replacenums[i]with eithernums[i],nums[(i - 1 + n) % n], ornums[(i + 1) % n].
Note that all the elements get replaced simultaneously.
Return the minimum number of seconds needed to make all elements in the array nums equal.
Example 1:
Input: nums = [1,2,1,2]
Output: 1
Explanation: We can equalize the array in 1 second in the following way:
- At 1st second, replace values at each index with [nums[3],nums[1],nums[3],nums[3]]. After replacement, nums = [2,2,2,2].
It can be proven that 1 second is the minimum amount of seconds needed for equalizing the array.Example 2:
Input: nums = [2,1,3,3,2]
Output: 2
Explanation: We can equalize the array in 2 seconds in the following way:
- At 1st second, replace values at each index with [nums[0],nums[2],nums[2],nums[2],nums[3]]. After replacement, nums = [2,3,3,3,3].
- At 2nd second, replace values at each index with [nums[1],nums[1],nums[2],nums[3],nums[4]]. After replacement, nums = [3,3,3,3,3].
It can be proven that 2 seconds is the minimum amount of seconds needed for equalizing the array.Example 3:
Input: nums = [5,5,5,5]
Output: 0
Explanation: We don't need to perform any operations as all elements in the initial array are the same.
Constraints:
1 <= n == nums.length <= 1051 <= nums[i] <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
This approach simulates the process of equalizing the array for every possible target value. The target values can only be the numbers initially present in the array. For each unique number in the input array, we treat it as a potential target. We then simulate the spread of this target value second by second until the entire array is filled with it. The minimum number of seconds over all possible targets is the answer.
Algorithm
- Find all unique numbers in
numsto identify potential target values. - Initialize
min_timeto a very large value. - For each unique number
target:- Create a temporary copy of the
numsarray. - Initialize
seconds = 0. - Loop until all elements in the temporary array are equal to
target:- Increment
seconds. - Create a new array
next_statefor the next second's configuration. - Find all indices
jwhere the temporary array currently has the valuetarget. - For each such index
j, the valuetargetcan spread toj-1,j, andj+1(circularly). Mark these positions as being able to becometargetin the next step. - Populate
next_state: if a position can becometarget, set it totarget. Otherwise, keep its old value. - Replace the temporary array with
next_statefor the next iteration.
- Increment
- After the loop terminates, update
min_time = min(min_time, seconds).
- Create a temporary copy of the
- Return
min_time.
Walkthrough
The core idea is to test every unique number from the input nums as the final value of the equalized array. For a chosen target value x, we start with the initial array and simulate the operation. In each second, a position i can become x if nums[i], nums[i-1], or nums[i+1] is already x. We repeat this process, counting the seconds, until the whole array becomes x. We do this for all unique initial numbers and take the minimum of the seconds counted.
// This approach is conceptual and too slow for the given constraints.import java.util.*; class Solution { public int minimumSeconds(List<Integer> nums) { int n = nums.size(); Set<Integer> uniqueElements = new HashSet<>(nums); int minSeconds = Integer.MAX_VALUE; for (int target : uniqueElements) { List<Integer> currentNums = new ArrayList<>(nums); int seconds = 0; while (true) { long countTarget = 0; for(int val : currentNums) { if (val == target) { countTarget++; } } if (countTarget == n) { break; } seconds++; List<Integer> nextNums = new ArrayList<>(currentNums); Set<Integer> canBecomeTarget = new HashSet<>(); for (int i = 0; i < n; i++) { if (currentNums.get(i) == target) { canBecomeTarget.add(i); canBecomeTarget.add((i - 1 + n) % n); canBecomeTarget.add((i + 1) % n); } } for (int idx : canBecomeTarget) { nextNums.set(idx, target); } currentNums = nextNums; } minSeconds = Math.min(minSeconds, seconds); } return minSeconds; }}Complexity
Time
O(U * n^2), where U is the number of unique elements and n is the array size. In the worst case, U can be n, leading to a complexity of O(n^3).
Space
O(n), where n is the size of the array. This is required to store copies of the array and helper data structures for each step of the simulation.
Trade-offs
Pros
Conceptually simple and directly follows the problem statement's description of the operation.
Cons
Extremely inefficient due to its high time complexity, making it impractical for the given constraints.
Involves repeated array copying and iteration within nested loops, leading to poor performance.
Likely to cause a 'Time Limit Exceeded' (TLE) error on most platforms.
Instead of simulating, we can analyze the problem mathematically. To make all elements equal to a value x, x must spread from its initial positions to cover the entire array. The time required is determined by the largest "gap" between any two consecutive occurrences of x in the circular array. The time to cover a gap of size d is floor(d/2) seconds. We can calculate this for every unique number in the array and find the minimum time.
Algorithm
- Use a
HashMap<Integer, List<Integer>>to store the indices of each number. The key will be the number, and the value will be a list of its indices. - Iterate through the input array
numsand populate theHashMap. The lists of indices will be naturally sorted as we iterate from left to right. - Initialize
min_secondsto a large value (e.g.,n). - Iterate through each number
xand its list of indicespositionsin theHashMap.- Let the list of indices be
p_0, p_1, ..., p_{k-1}. - Calculate the maximum gap between consecutive occurrences. Initialize
max_gap = 0. - First, calculate the circular gap between the last and first occurrence:
gap = (n - p_{k-1}) + p_0. Updatemax_gapwith this value. - Then, iterate from
i = 1tok-1and updatemax_gap = max(max_gap, p_i - p_{i-1}). - The time required for this target
xistime_for_x = max_gap / 2(using integer division). - Update
min_seconds = min(min_seconds, time_for_x).
- Let the list of indices be
- Return
min_seconds.
Walkthrough
The key insight is that the problem can be solved independently for each unique number present in the initial array. For any target value x, the time it takes to make the whole array x depends on the maximum distance any element has to "travel". An element at index i can become x in s seconds if its circular distance to an initial occurrence of x is at most s. To make the whole array x, every position must be within s seconds of an initial x. This means s must be at least the maximum of these minimum distances, which is determined by the midpoint of the largest gap between two consecutive occurrences of x. If the largest gap has a length of max_gap, the time required is floor(max_gap / 2).
import java.util.*; class Solution { public int minimumSeconds(List<Integer> nums) { int n = nums.size(); Map<Integer, List<Integer>> pos = new HashMap<>(); for (int i = 0; i < n; i++) { pos.computeIfAbsent(nums.get(i), k -> new ArrayList<>()).add(i); } int minSeconds = Integer.MAX_VALUE; for (List<Integer> indices : pos.values()) { int maxGap = 0; // First, calculate the circular gap between the last and first occurrence maxGap = indices.get(0) + n - indices.get(indices.size() - 1); // Then, calculate the gaps between consecutive occurrences for (int i = 1; i < indices.size(); i++) { maxGap = Math.max(maxGap, indices.get(i) - indices.get(i - 1)); } // The time needed is half of the largest gap minSeconds = Math.min(minSeconds, maxGap / 2); } return minSeconds; }}Complexity
Time
O(n). Populating the HashMap takes O(n). Iterating through the map's values involves visiting each index once across all lists, which is also O(n) in total.
Space
O(n). In the worst case, if all elements are unique, the HashMap will store `n` keys. If all elements are the same, it will store one key with a list of `n` indices. In both scenarios, the total space used is proportional to `n`.
Trade-offs
Pros
Highly efficient with linear time complexity, easily passing the given constraints.
Avoids costly simulation by using a direct mathematical formula.
Solves the problem in a single pass after grouping indices.
Cons
Requires extra space for the HashMap to store indices, which can be up to O(n).
Solutions
Solution
class Solution {public int minimumSeconds(List<Integer> nums) { Map<Integer, List<Integer>> d = new HashMap<>(); int n = nums.size(); for (int i = 0; i < n; ++i) { d.computeIfAbsent(nums.get(i), k->new ArrayList<>()).add(i); } int ans = 1 << 30; for (List<Integer> idx : d.values()) { int m = idx.size(); int t = idx.get(0) + n - idx.get(m - 1); for (int i = 1; i < m; ++i) { t = Math.max(t, idx.get(i) - idx.get(i - 1)); } ans = Math.min(ans, t / 2); } return ans; }}class Solution {public: int minimumSeconds(vector<int> &nums) { unordered_map<int, vector<int>> d; int n = nums.size(); for (int i = 0; i < n; ++i) { d[nums[i]].push_back(i); } int ans = 1 << 30; for (auto &[_, idx] : d) { int m = idx.size(); int t = idx[0] + n - idx[m - 1]; for (int i = 1; i < m; ++i) { t = max(t, idx[i] - idx[i - 1]); } ans = min(ans, t / 2); } return ans; }};class Solution: def minimumSeconds(self, nums: List[int]) -> int: d = defaultdict(list) for i, x in enumerate(nums): d[x]. append(i) ans = inf n = len(nums) for idx in d . values(): t = idx[0] + n - idx[- 1] for i, j in pairwise(idx): t = max(t, j - i) ans = min(ans, t // 2) return ansVideo 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.