3431. Minimum Unlocked Indices to Sort Nums 🔒
Problem Description
You are given an array nums
consisting of integers between 1 and 3, and a binary array locked
of the same size.
We consider nums
sortable if it can be sorted using adjacent swaps, where a swap between two indices i
and i + 1
is allowed if nums[i] - nums[i + 1] == 1
and locked[i] == 0
.
In one operation, you can unlock any index i
by setting locked[i]
to 0.
Return the minimum number of operations needed to make nums
sortable. If it is not possible to make nums
sortable, return -1.
Intuition
To solve this problem, we need to determine how the array nums
can be sorted given the constraints on swaps dictated by the locked
array. The core idea is that nums
is sortable if we can rearrange it such that every occurrence of the number 3 comes after every occurrence of the number 1.
We identify the first occurrence of the number 2 (first2
) and 3 (first3
), and the last occurrence of the number 1 (last1
) and 2 (last2
). The key observation is that if first3
is less than last1
, then the numbers are not in the correct order to be sorted—3s are incorrectly placed before 1s—and hence sorting is impossible without swaps.
The main technique used to arrive at a solution involves counting operations. Specifically, we require every index in the range [first2, last1)
or [first3, last2)
in the locked
array to be unlocked (locked[i] == 0
) to allow sorting swaps. Thus, we count all positions in these ranges that are locked
, indicating the minimum number of operations needed to unlock these positions.
Solution Approach
To implement the solution, follow these steps:
-
Initialization:
- Start by determining the length of the
nums
array, denoted asn
. - Initialize
first2
andfirst3
ton
(signifying unrealistic high index positions) andlast1
andlast2
to-1
(indicating nonexistent low index positions).
- Start by determining the length of the
-
Identify Critical Indices:
- Traverse through the
nums
array and for each indexi
with valuex
:- If
x == 1
, updatelast1
toi
(mark the last occurrence of 1). - If
x == 2
, update bothfirst2
to the minimum of currentfirst2
andi
, andlast2
toi
(track boundaries for 2). - If
x == 3
, updatefirst3
to the minimum of currentfirst3
andi
(mark the initial occurrence of 3).
- If
- Traverse through the
-
Check Sortability Condition:
- If
first3 < last1
, it indicates that at least one 3 is incorrectly before a 1, making sorting impossible under given constraints. Thus, return-1
.
- If
-
Count Required Unlocks:
- Traverse the
locked
array and calculate how many indices fall within the ranges[first2, last1)
or[first3, last2)
that are locked. This determines the number of unlock operations needed. - For each index
i
inlocked
, if it is locked and falls within either of these critical ranges, increment a counter.
- Traverse the
-
Return the Count:
- The counter gives the minimum number of unlock operations needed to allow
nums
to become sortable under the specified conditions.
- The counter gives the minimum number of unlock operations needed to allow
The solution uses a simple traversal of arrays and manipulation of indices, resulting in a time complexity of O(n)
, where n
is the length of the input arrays.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Consider the following example to understand the solution approach:
Inputs:
nums = [2, 3, 1, 2, 3]
locked = [0, 1, 1, 0, 1]
Step-by-Step Solution:
-
Initialization:
n = 5
(length of the array).- Initialize
first2
andfirst3
to5
(an index beyond the array length). - Initialize
last1
andlast2
to-1
(nonexistent index).
-
Identify Critical Indices:
- Traverse
nums
:- For
i = 0
,nums[0] = 2
:first2 = min(5, 0) = 0
last2 = 0
- For
i = 1
,nums[1] = 3
:first3 = min(5, 1) = 1
- For
i = 2
,nums[2] = 1
:last1 = 2
- For
i = 3
,nums[3] = 2
:last2 = 3
- For
i = 4
,nums[4] = 3
:first3
remains1
- For
- Traverse
-
Check Sortability Condition:
- Since
first3 (1) < last1 (2)
, the 3 at index 1 comes before the 1 at index 2, indicating sorting is currently impossible without unlocking.
- Since
-
Count Required Unlocks:
- Traverse the
locked
array and check indices in the critical ranges:- Range
[first2, last1)
is[0, 2)
→ indices 0 and 1. - Range
[first3, last2)
is[1, 3)
→ indices 1 and 2.
- Range
- Check locked statuses:
i = 0
:locked[0] = 0
(already unlocked).i = 1
:locked[1] = 1
(locked in the range), increment counter to 1.i = 2
:locked[2] = 1
(locked in the range), increment counter to 2.
- Traverse the
-
Return the Count:
- A minimum of 2 unlocking operations is necessary to make
nums
sortable.
- A minimum of 2 unlocking operations is necessary to make
In this walk-through, unlocking indices 1 and 2 allows adjacent swaps to eventually position all 3s after all 1s, fulfilling the sorting condition.
Solution Implementation
1from typing import List
2
3class Solution:
4 def minUnlockedIndices(self, nums: List[int], locked: List[int]) -> int:
5 n = len(nums)
6
7 # Initialize the positions for our number markers
8 first2 = first3 = n
9 last1 = last2 = -1
10
11 # Loop through the nums list to track position markers
12 for i, x in enumerate(nums):
13 if x == 1:
14 last1 = i # Keep track of the last position of '1'
15 elif x == 2:
16 first2 = min(first2, i) # Track the first position of '2'
17 last2 = i # Track the last position of '2'
18 elif x == 3:
19 first3 = min(first3, i) # Track the first position of '3'
20
21 # If the first occurrence of '3' is before the last occurrence of '1', return -1
22 if first3 < last1:
23 return -1
24
25 # Calculate the number of locked indices between the specified ranges
26 return sum(
27 is_locked and (first2 <= i < last1 or first3 <= i < last2)
28 for i, is_locked in enumerate(locked)
29 )
30
1class Solution {
2 public int minUnlockedIndices(int[] nums, int[] locked) {
3 int n = nums.length;
4
5 // Initializing indices to track the positions of first occurrences and last occurrences of numbers
6 int firstTwoIndex = n, firstThreeIndex = n;
7 int lastOneIndex = -1, lastTwoIndex = -1;
8
9 // Loop through the array to find the required indices
10 for (int i = 0; i < n; ++i) {
11 if (nums[i] == 1) {
12 // Update the last occurrence index for number 1
13 lastOneIndex = i;
14 } else if (nums[i] == 2) {
15 // Update the first occurrence index for number 2 and last occurrence index
16 firstTwoIndex = Math.min(firstTwoIndex, i);
17 lastTwoIndex = i;
18 } else if (nums[i] == 3) {
19 // Update the first occurrence index for number 3
20 firstThreeIndex = Math.min(firstThreeIndex, i);
21 }
22 }
23
24 // Verify if a number 3 appears before the last occurrence of number 1
25 if (firstThreeIndex < lastOneIndex) {
26 return -1; // Condition that makes unlocking impossible
27 }
28
29 // Initialize the counter for unlocked indices
30 int unlockedIndices = 0;
31
32 // Loop again to count the necessary unlocked indices according to the conditions
33 for (int i = 0; i < n; ++i) {
34 boolean betweenTwoAndLastOne = firstTwoIndex <= i && i < lastOneIndex;
35 boolean betweenThreeAndLastTwo = firstThreeIndex <= i && i < lastTwoIndex;
36
37 // Count the locked indices that are between the necessary range
38 if (locked[i] == 1 && (betweenTwoAndLastOne || betweenThreeAndLastTwo)) {
39 ++unlockedIndices;
40 }
41 }
42
43 return unlockedIndices; // Return the count of minimum required unlocked indices
44 }
45}
46
1class Solution {
2public:
3 int minUnlockedIndices(vector<int>& nums, vector<int>& locked) {
4 int n = nums.size();
5
6 // Initialize variables to track the first occurrence of "2" and "3"
7 // and the last occurrence of "1" and "2"
8 int firstTwoIndex = n, firstThreeIndex = n;
9 int lastOneIndex = -1, lastTwoIndex = -1;
10
11 // Find the positions of the first "2", first "3"
12 // and last "1", last "2" in the array
13 for (int i = 0; i < n; ++i) {
14 if (nums[i] == 1) {
15 lastOneIndex = i;
16 } else if (nums[i] == 2) {
17 firstTwoIndex = std::min(firstTwoIndex, i);
18 lastTwoIndex = i;
19 } else if (nums[i] == 3) {
20 firstThreeIndex = std::min(firstThreeIndex, i);
21 }
22 }
23
24 // If there is a "3" that appears before the last "1",
25 // return -1 (invalid configuration)
26 if (firstThreeIndex < lastOneIndex) {
27 return -1;
28 }
29
30 int unlockedCount = 0;
31 // Count the locked indices that are positioned between first "2" and last "1",
32 // or between first "3" and last "2".
33 for (int i = 0; i < n; ++i) {
34 if (locked[i] == 1 &&
35 ((firstTwoIndex <= i && i < lastOneIndex) ||
36 (firstThreeIndex <= i && i < lastTwoIndex))) {
37 ++unlockedCount;
38 }
39 }
40
41 return unlockedCount;
42 }
43};
44
1// Function to find the minimum number of locked indices that need to be adjusted
2function minUnlockedIndices(nums: number[], locked: number[]): number {
3 const n = nums.length;
4 let [first2, first3] = [n, n]; // Initialize the first occurrence indices of 2 and 3
5 let [last1, last2] = [-1, -1]; // Initialize the last occurrence indices of 1 and 2
6
7 // Traverse the nums array to find the first and last occurrences of 1, 2, and the first occurrence of 3
8 for (let i = 0; i < n; i++) {
9 if (nums[i] === 1) {
10 last1 = i; // Update the last occurrence of 1
11 } else if (nums[i] === 2) {
12 first2 = Math.min(first2, i); // Update the first occurrence of 2
13 last2 = i; // Update the last occurrence of 2
14 } else if (nums[i] === 3) {
15 first3 = Math.min(first3, i); // Update the first occurrence of 3
16 }
17 }
18
19 // If the first occurrence of 3 is before the last occurrence of 1, return -1
20 if (first3 < last1) {
21 return -1;
22 }
23
24 let ans = 0; // Initialize the answer for minimum number of locked indices
25 // Check for locked indices that must be adjusted to maintain order
26 for (let i = 0; i < n; i++) {
27 // Count locked indices between first2 and last1, or between first3 and last2
28 if (locked[i] === 1 && ((first2 <= i && i < last1) || (first3 <= i && i < last2))) {
29 ans++;
30 }
31 }
32
33 return ans; // Return the count of minimum locked indices needed
34}
35
Time and Space Complexity
The time complexity of the code is O(n)
, where n
is the length of the array nums
. This is because the algorithm iterates through the nums
array and the locked
array each once. Specifically, the first loop traverses the nums
array to determine positions related to elements 1
, 2
, and 3
, while the second loop calculates the sum based on the locked
array. Each of these operations takes linear time proportional to the size of the arrays.
The space complexity is O(1)
, as the algorithm uses a constant amount of extra space regardless of the size of the input arrays. The variables first2
, first3
, last1
, and last2
are used to store index positions and do not scale with the input size.
Learn more about how to find time and space complexity quickly.
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!