Facebook Pixel

3431. Minimum Unlocked Indices to Sort Nums 🔒

MediumArrayHash Table
Leetcode Link

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:

  1. Initialization:

    • Start by determining the length of the nums array, denoted as n.
    • Initialize first2 and first3 to n (signifying unrealistic high index positions) and last1 and last2 to -1 (indicating nonexistent low index positions).
  2. Identify Critical Indices:

    • Traverse through the nums array and for each index i with value x:
      • If x == 1, update last1 to i (mark the last occurrence of 1).
      • If x == 2, update both first2 to the minimum of current first2 and i, and last2 to i (track boundaries for 2).
      • If x == 3, update first3 to the minimum of current first3 and i (mark the initial occurrence of 3).
  3. 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.
  4. 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 in locked, if it is locked and falls within either of these critical ranges, increment a counter.
  5. Return the Count:

    • The counter gives the minimum number of unlock operations needed to allow nums to become sortable under the specified conditions.

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 Evaluator

Example 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:

  1. Initialization:

    • n = 5 (length of the array).
    • Initialize first2 and first3 to 5 (an index beyond the array length).
    • Initialize last1 and last2 to -1 (nonexistent index).
  2. 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 remains 1
  3. 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.
  4. 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.
    • 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.
  5. Return the Count:

    • A minimum of 2 unlocking operations is necessary to make nums sortable.

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.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

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

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More