Talentd
FleetCode
Make and follow sheets.Sign in

Earliest Second to Mark Indices II solution and explanation

HardArrayBinary SearchGreedyHeap (Priority Queue)6 min read22.6% acceptanceUpdated 26 Mar 2026
3049. Earliest Second to Mark Indices II | Weekly Leetcode 386codingMohan · 1:01:59 · 1K viewsWatch on YouTube
3 more videos on this problem
  1. 3049. Earliest Second to Mark Indices II | Weekly Leetcode 386codingMohan · 1:01:59 · 1K views
  2. 3049. Earliest Second to Mark Indices II (Leetcode Hard)Programming Live with Larry · 1:05:56 · 465 views
  3. 【每日一题】LeetCode 3049. Earliest Second to Mark Indices IIHuifeng Guan · 36:55 · 459 views
  4. LeetCode 3049 - Earliest Second to Mark Indices II - PythonAlpha-Code · 34:52 · 183 views

The problem

You are given two 1-indexed integer arrays, nums and, changeIndices, having lengths n and m, respectively.

Initially, all indices in nums are unmarked. Your task is to mark all indices in nums.

In each second, s, in order from 1 to m (inclusive), you can perform one of the following operations:

  • Choose an index i in the range [1, n] and decrement nums[i] by 1.
  • Set nums[changeIndices[s]] to any non-negative value.
  • Choose an index i in the range [1, n], where nums[i] is equal to 0, and mark index i.
  • Do nothing.

Return an integer denoting the earliest second in the range [1, m] when all indices in nums can be marked by choosing operations optimally, or -1 if it is impossible.

Example 1:

Input: nums = [3,2,3], changeIndices = [1,3,2,2,2,2,3]
Output: 6
Explanation: In this example, we have 7 seconds. The following operations can be performed to mark all indices:
Second 1: Set nums[changeIndices[1]] to 0. nums becomes [0,2,3].
Second 2: Set nums[changeIndices[2]] to 0. nums becomes [0,2,0].
Second 3: Set nums[changeIndices[3]] to 0. nums becomes [0,0,0].
Second 4: Mark index 1, since nums[1] is equal to 0.
Second 5: Mark index 2, since nums[2] is equal to 0.
Second 6: Mark index 3, since nums[3] is equal to 0.
Now all indices have been marked.
It can be shown that it is not possible to mark all indices earlier than the 6th second.
Hence, the answer is 6.

Example 2:

Input: nums = [0,0,1,2], changeIndices = [1,2,1,2,1,2,1,2]
Output: 7
Explanation: In this example, we have 8 seconds. The following operations can be performed to mark all indices:
Second 1: Mark index 1, since nums[1] is equal to 0.
Second 2: Mark index 2, since nums[2] is equal to 0.
Second 3: Decrement index 4 by one. nums becomes [0,0,1,1].
Second 4: Decrement index 4 by one. nums becomes [0,0,1,0].
Second 5: Decrement index 3 by one. nums becomes [0,0,0,0].
Second 6: Mark index 3, since nums[3] is equal to 0.
Second 7: Mark index 4, since nums[4] is equal to 0.
Now all indices have been marked.
It can be shown that it is not possible to mark all indices earlier than the 7th second.
Hence, the answer is 7.

Example 3:

Input: nums = [1,2,3], changeIndices = [1,2,3]
Output: -1
Explanation: In this example, it can be shown that it is impossible to mark all indices, as we don't have enough seconds. 
Hence, the answer is -1.

Constraints:

  • 1 <= n == nums.length <= 5000
  • 0 <= nums[i] <= 109
  • 1 <= m == changeIndices.length <= 5000
  • 1 <= changeIndices[i] <= n

Approach overview

Problem Overview: You are given an array representing the work required before an index can be marked and a timeline that specifies which index becomes markable at each second. The task is to determine the earliest second when every index can be marked while respecting the operation constraints.

Approach 1: Dynamic Programming (O(n * m) time, O(n * m) space)

The dynamic programming formulation models each second as a decision point: either spend the second reducing remaining work for some index or perform a marking operation when the index becomes available. The state tracks how many indices have been processed and how many seconds have been used so far. By iterating through the timeline and updating transitions, the algorithm determines whether all indices can be completed within a given prefix of time. This approach is conceptually straightforward and clearly models the constraints, but the state space grows quickly with larger inputs.

This approach is useful for reasoning about correctness because it explicitly represents every choice. However, the quadratic state expansion makes it impractical for large timelines. In interview settings it mainly serves as a stepping stone toward a greedy or search-based optimization.

Approach 2: Binary Search + Greedy Heap Scheduling (O(m log n) time, O(n) space)

The optimal strategy treats each marking opportunity as a deadline and checks whether the required preparation work can be scheduled before it. Perform a binary search over the answer: assume the earliest valid second is t, then verify if marking all indices within the first t seconds is feasible.

During the feasibility check, scan the timeline and treat each appearance of an index as a potential deadline to mark it. The amount of preparation work required for that index must be completed before the mark happens. Maintain a heap (priority queue) of chosen tasks and track the total time spent preparing them. If the schedule exceeds the available time, remove the most expensive preparation using a max-heap style adjustment. This greedy step ensures the schedule always keeps the set of tasks with the smallest total cost.

The combination of greedy scheduling and binary search drastically reduces the search space. Instead of simulating every possibility, the algorithm only verifies feasibility for candidate times. This leads to an efficient solution that scales to large inputs.

Recommended for interviews: The binary search with greedy heap check is the expected solution. Explaining the DP formulation first shows you understand the state transitions, but implementing the heap-based feasibility check demonstrates strong algorithmic reasoning and optimization skills.

Approach 1: Dynamic Programming Approach

This approach leverages the concept of Dynamic Programming where we break down the problem into smaller subproblems and solve them independently, but store the results to avoid redundant computations. This approach is particularly beneficial when the same subproblem arises multiple times.

def solveDP(n, dp):
    if n <= 1:
        return n
    if dp[n] != -1:
        return dp[n]
    dp[n] = solveDP(n - 1, dp) + solveDP(n - 2, dp)
    return dp[n]

def main():
    n = 10
    dp = [-1] * (n + 1)
    print(solveDP(n, dp))

main()
import java.util.Arrays;

public class FibDP {
    private static int[] dp = new int[1000];

    public static int solveDP(int n) {
        if (n <= 1) return n;
        if (dp[n] != -1) return dp[n];
        dp[n] = solveDP(n - 1) + solveDP(n - 2);
        return dp[n];
    }

    public static void main(String[] args) {
        int n = 10;
        Arrays.fill(dp, -1);
        System.out.println(solveDP(n));
    }
}
#include <iostream>
#include <vector>

std::vector<int> dp(1000, -1);

int solveDP(int n) {
    if (n <= 1) return n;
    if (dp[n] != -1) return dp[n];
    dp[n] = solveDP(n - 1) + solveDP(n - 2);
    return dp[n];
}

int main() {
    int n = 10;
    std::cout << solveDP(n);
    return 0;
}
function solveDP(n, dp) {
    if (n <= 1) return n;
    if (dp[n] !== -1) return dp[n];
    dp[n] = solveDP(n - 1, dp) + solveDP(n - 2, dp);
    return dp[n];
}

function main() {
    const n = 10;
    const dp = Array(n + 1).fill(-1);
    console.log(solveDP(n, dp));
}

main();
#include <stdio.h>
#define MAX 1000

int dp[MAX];

int solveDP(int n) {
    if (n <= 1) return n;
    if (dp[n] != -1) return dp[n];
    dp[n] = solveDP(n - 1) + solveDP(n - 2);
    return dp[n];
}

int main() {
    int n = 10;
    for (int i = 0; i <= n; i++) dp[i] = -1;
    printf("%d", solveDP(n));
    return 0;
}
using System;

class FibDP {
    static int[] dp = new int[1000];

    public static int SolveDP(int n) {
        if (n <= 1) return n;
        if (dp[n] != -1) return dp[n];
        dp[n] = SolveDP(n - 1) + SolveDP(n - 2);
        return dp[n];
    }

    static void Main() {
        int n = 10;
        Array.Fill(dp, -1);
        Console.WriteLine(SolveDP(n));
    }
}

Python's solution is very readable and makes use of a list to prevent recalculations of subproblems. The memoization ensures that each subproblem is computed once, leading to a more efficient program in terms of time complexity.

Complexity

Time Complexity: O(n)
Space Complexity: O(n)

Try this approach in the editor

Approach 2: Iterative Approach with Space Optimization

The iterative approach calculates Fibonacci numbers using only two variables to eliminate extra space usage. This approach keeps track of only the last two Fibonacci numbers computed, thus reducing space complexity to O(1).

def fibIter(n):
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

def main():
    n = 10
    print(fibIter(n))

main()
public class FibIter {
    public static int fibIter(int n) {
        if (n <= 1) return n;
        int a = 0, b = 1, c;
        for (int i = 2; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
        return b;
    }

    public static void main(String[] args) {
        int n = 10;
        System.out.println(fibIter(n));
    }
}
#include <iostream>

int fibIter(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1, c;
    for (int i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

int main() {
    int n = 10;
    std::cout << fibIter(n);
    return 0;
}
function fibIter(n) {
    if (n <= 1) return n;
    let a = 0, b = 1, c;
    for (let i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

function main() {
    const n = 10;
    console.log(fibIter(n));
}

main();
#include <stdio.h>

int fibIter(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1, c;
    for (int i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

int main() {
    int n = 10;
    printf("%d", fibIter(n));
    return 0;
}
using System;

class FibIter {
    public static int FibIterative(int n) {
        if (n <= 1) return n;
        int a = 0, b = 1, c;
        for (int i = 2; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
        return b;
    }

    static void Main() {
        int n = 10;
        Console.WriteLine(FibIterative(n));
    }
}

The Python implementation maintains constant space while calculating the Fibonacci numbers. After initializing the first two numbers, the solution iterates up to `n`, updating the two tracked values at each step.

Complexity

Time Complexity: O(n)
Space Complexity: O(1)

Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming Approach

Time Complexity: O(n)
Space Complexity: O(n)

Iterative Approach with Space Optimization

Time Complexity: O(n)
Space Complexity: O(1)

Complexity analysis

ApproachTimeSpaceWhen to Use
Dynamic ProgrammingO(n * m)O(n * m)Good for understanding the state transitions and verifying correctness on smaller inputs
Binary Search + Greedy HeapO(m log n)O(n)Best for large constraints; efficiently finds earliest feasible time
Iterative Greedy with Space OptimizationO(m log n)O(n)When memory is limited but the same greedy feasibility logic is required

Frequently asked questions

Is Earliest Second to Mark Indices II easy or hard?

Earliest Second to Mark Indices II is classified as a Hard problem. It combines binary search, greedy reasoning, and heap-based scheduling, which requires strong understanding of algorithm design and feasibility checking techniques.

Earliest Second to Mark Indices II Python/Java solution

Implement the algorithm by binary searching the earliest feasible second and using a priority queue during the feasibility check. Python typically uses heapq, while Java implementations rely on PriorityQueue to manage preparation costs efficiently.

How to solve Earliest Second to Mark Indices II in O(m log n)?

Binary search the earliest second t where all indices might be marked. For each candidate t, scan the first t seconds and treat each mark opportunity as a deadline. Use a priority queue to keep track of preparation costs and greedily drop the most expensive tasks if the schedule exceeds available time.

What is the best approach for Earliest Second to Mark Indices II?

Binary search combined with a greedy scheduling check using a priority queue is the most efficient approach. Binary search determines the earliest possible second, while the heap-based feasibility check ensures preparation work fits before each marking deadline. This reduces the complexity to about O(m log n).

Is Earliest Second to Mark Indices II asked at Google/Amazon/Meta?

Problems involving binary search on the answer combined with greedy scheduling frequently appear in interviews at companies like Google, Amazon, and Meta. Variants that mix deadlines, heaps, and feasibility checks are common in high-level algorithm rounds.

What data structure is used in Earliest Second to Mark Indices II?

The key data structure is a heap (priority queue). It maintains the set of preparation tasks currently scheduled and allows efficient removal of the most expensive task when the total work exceeds the available time window.

What is the time complexity of Earliest Second to Mark Indices II?

The optimal solution runs in O(m log n) time, where m is the number of seconds in the timeline and n is the number of indices. The binary search tests multiple candidate times, and each feasibility check uses heap operations that cost O(log n). Space complexity is O(n).

Comments

Now write it yourselfClose this page and solve Earliest Second to Mark Indices II in the editor, without looking.
Practise Earliest Second to Mark Indices II