Master this topic with zero to advance depth.
Jump Game II
You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0].
Each element nums[i] represents the maximum length of a forward jump from index i. In other words, if you are at nums[i], you can jump to any nums[i + j] where:
0 <= j <= nums[i] andi + j < nReturn the minimum number of jumps to reach nums[n - 1]. The test cases are generated such that you can reach nums[n - 1].
Index: 0 1 2 3 4
Value: 2 3 1 1 4
Step 1: At index 0 (val 2). Can jump to 1 or 2.
Step 2: From index 1 (val 3). Can jump to 4 (Goal!).
Total Jumps: 2Examples
Jump 1 step from index 0 to 1, then 3 steps to the last index.
Intuition
Define dp[i] as the minimum jumps to reach the end starting from index i. To find dp[i], we 'look ahead' at all reachable indices j and pick the best one.
dp[i] is the min jumps from index i to n-1.dp[n-1] = 0.dp[i] = 1 + min(dp[j]) for all i < j <= i + nums[i].dp with infinity.Detailed Dry Run
nums = [2, 3, 1, 1, 4]
| i | Range | Best Next | dp[i] |
|---|---|---|---|
| 4 | - | - | 0 |
| 3 | [4] | idx 4 (0) | 1 |
| 2 | [3] | idx 3 (1) | 2 |
| 1 | [2,3,4] | idx 4 (0) | 1 |
| 0 | [1,2] | idx 1 (1) | 2 |
Intuition
Each jump can be seen as a 'level' in BFS. Indices reachable in 1 jump are Level 1, indices reachable in 2 jumps are Level 2, etc. Use a queue to traverse until is reached.
(index, levels).visited set to avoid cycles/redundancy.Detailed Dry Run
nums = [2, 3, 1, 1, 4] q: [(0,0)]. Pop (0,0). Push (1,1), (2,1). Vis={0,1,2}. Pop (1,1). Push (3,2), (4,2). Reach end! Return 2.
Intuition
Maintain a 'window' of reachable indices for the current number of jumps. When we reach the end of the current window, we increment jump count and move to the farthest point reachable from that window.
jumps, curEnd, farthest variables.farthest = max(farthest, i + nums[i]).i == curEnd, update curEnd = farthest, jumps++.Detailed Dry Run
[2,3,1,1,4] i=0: f=2. i==curEnd(0). end=2, jumps=1. i=1: f=4. OK. i=2: f=4. i==curEnd(2). end=4, jumps=2. Result: 2.
Gas Station
There are n gas stations along a circular route, where the amount of gas at the station is gas[i].
You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from the station to its next station.
Given two integer arrays gas and cost, return the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return -1.
If you start at index , you must be able to visit .
Examples
Start at station 3 (index 3). Fill 4 units of gas. Your tank = 4. Cost to next = 1. Remaining = 3. Final tank check covers the loop.
Intuition
Try starting from every single gas station and simulate the journey. If you can complete the circle without the tank becoming negative, that's your starting station.
Stations: [S0, S1, S2]
Try S0: S0 -> S1 -> S2 -> S0 (Check tank >= 0 at each step)
Try S1: S1 -> S2 -> S0 -> S1
Try S2: S2 -> S0 -> S1 -> S2start from to .start, initialize tank = 0.i from to to visit all stations using (start + i) % n.tank < 0 at any point, stop and try next start.Detailed Dry Run
gas=[1,2], cost=[2,1] S0: tank=1-2=-1. FAIL. S1: tank=2-1=1. Next: tank=1+(1-2)=0. SUCCESS!
Intuition
Before attempting a simulation, check if a solution even exists. If , it is mathematically impossible to complete the circuit regardless of where you start.
totalGas and totalCost.totalGas < totalCost, return -1.Detailed Dry Run
gas=[1,2,3], cost=[2,2,2]. Total gas=6, cost=6. Possible. Start S0: tank=1-2=-1. FAIL. Reset start to S1. Start S1: tank=2-2=0. OK. Start S2: tank=0+(3-2)=1. OK. Return S1.
Intuition
We can combine the total sum check into the main loop. Track totalDiff as we go. If totalDiff is negative at the end, return -1. Otherwise, the last valid start candidate is guaranteed to work.
Indices: [ 0, 1, 2, 3, 4]
Diffs: [-2, -2, -2, 3, 3]
Loop:
i=0: tank=-2. Fail. start=1, tank=0.
i=1: tank=-2. Fail. start=2, tank=0.
i=2: tank=-2. Fail. start=3, tank=0.
i=3: tank=3. OK.
i=4: tank=6. OK.
TotalDiff = 0 >= 0. Result: start index 3.totalTank to keep running sum of all (gas[i] - cost[i]).currentTank to track gas from current start candidate.currentTank drops below 0, the current start (and all stations between it and i) is invalid. Reset currentTank and set start = i + 1.totalTank >= 0 ? start : -1.Detailed Dry Run
gas=[1,2,3,4,5], cost=[3,4,5,1,2]
| i | diff | curTank | totalTank | start |
|---|---|---|---|---|
| 0 | -2 | -2 (Reset) | -2 | 1 |
| 1 | -2 | -2 (Reset) | -4 | 2 |
| 2 | -2 | -2 (Reset) | -6 | 3 |
| 3 | 3 | 3 | -3 | 3 |
| 4 | 3 | 6 | 0 | 3 |
| totalTank=0. Return 3. |
Partition Labels
You are given a string s. We want to partition the string into as many parts as possible so that each letter appears in at most one part.
Return a list of integers representing the size of these parts.
s = "ababcbacadefegdehijhklij"
Letter 'a' appears at indices [0, 2, 6, 8]
If we take 'a', we MUST take everything up to index 8.
Inside [0, 8], we have 'b' (ends at 5), 'c' (ends at 7).
Max end index for characters in [0, 8] is index 8.
Partition 1: "ababcbaca" (Size 9)Examples
The partition is "ababcbaca", "defegde", "hijhklij".
Intuition
For every partition starting at i, we find the last occurrence of s[i]. This is our initial end. Then, for every character between i and end, we update end if that character's last occurrence is even further.
i from 0 to .start = i, end = lastOccurrence(s[i]).j from i to end:
end = max(end, lastOccurrence(s[j])).j reaches end, the partition is complete. Add end - start + 1 to result.i to end + 1.Detailed Dry Run
s = "abaccb"
Intuition
Treat each character as an interval from its first occurrence to its last occurrence. Any overlapping intervals MUST be merged into a single partition. This reduces the problem to a standard interval merging task.
first and last occurrence for each character 'a'-'z'.[first, last] for characters that appear in the string.Detailed Dry Run
s = "abaccb" Intervals: a[0,2], b[1,5], c[3,4]
Intuition
As we scan the string, we keep track of the maximum last index seen so far for characters in the current partition. When our current index i catches up to this last index, we've found a valid partition.
lastIndex for every character in a map/array.start and end pointers.i from 0 to :
end = max(end, lastIndex[s[i]]).i == end:
i - start + 1.start = i + 1.Detailed Dry Run
s = "ababcbaca..."
| i | char | lastIndex[char] | end | Action |
|---|---|---|---|---|
| 0 | a | 8 | 8 | - |
| 1 | b | 5 | 8 | - |
| ... | ... | ... | 8 | - |
| 8 | a | 8 | 8 | i == end! Size = 8-0+1 = 9. start = 9. |
Minimum Number of Arrows to Burst Balloons
There are some spherical balloons taped onto a flat wall that represents the XY-plane. The balloons are represented as a 2D integer array points where points[i] = [x_start, x_end] denotes a balloon whose horizontal diameter stretches between x_start and x_end.
An arrow can be shot up exactly vertically from different points along the x-axis. A balloon with x_start and x_end is burst by an arrow shot at x if x_start <= x <= x_end. There is no limit to the number of arrows that can be shot.
Return the minimum number of arrows that must be shot to burst all balloons.
Points: [[10,16], [2,8], [1,6], [7,12]]
Sort by end points:
[1, 6], [2, 8], [7, 12], [10, 16]
Arrow 1: Shoot at x=6. Bursts [1,6] and [2,8].
Arrow 2: Shoot at x=12. Bursts [7,12] and [10,16].
Total: 2 arrows.Examples
One arrow at 6 bursts [1,6] and [2,8]. Another arrow at 12 bursts [7,12] and [10,16].
Intuition
If we don't know the optimal sorting trick, a natural brute-force thought is: 'Let's find the coordinate that bursts the maximum number of balloons right now, shoot an arrow there, remove the burst balloons, and repeat until no balloons are left.' The candidate coordinates to shoot at are simply the start and end points of the remaining balloons.
arrows, filter out the burst balloons, and repeat.Detailed Dry Run
points = [[1,6],[2,8],[7,12]]
Intuition
If we sort by start point, we can maintain a 'current intersection' range. Every new balloon must overlap with this intersection. If it doesn't, we shoot an arrow and start a new intersection range.
start point.[currentStart, currentEnd].[bStart, bEnd]:
bStart <= currentEnd (they overlap):
currentEnd = min(currentEnd, bEnd).currentEnd = bEnd and increment arrows.Detailed Dry Run
pointsSorted = [[1,6],[2,8],[7,12]]
Intuition
To minimize arrows, we want each arrow to burst as many balloons as possible. By sorting by the end point, we fix the rightmost boundary for an arrow. If a balloon's start point is within the current arrow's range (i.e., start <= current_arrow_pos), it is burst. Otherwise, we need a new arrow at the new balloon's end point.
Sorting by the end point ensures that the current balloon we are considering is the one that 'expires' soonest. Putting an arrow at its end point is the best greedy choice because it covers this balloon AND has the maximum chance of covering future balloons that start before this end point.
Detailed Dry Run
points = [[10,16], [2,8], [1,6], [7,12]] After Sort: [[1,6], [2,8], [7,12], [10,16]]
| Step | Balloon | Range | lastArrowAt | arrows | Action |
|---|---|---|---|---|---|
| 1 | [1, 6] | [1, 6] | 6 | 1 | Shoot at 6 (end of first) |
| 2 | [2, 8] | [2, 8] | 6 | 1 | 2 <= 6? YES (Burst!) |
| 3 | [7, 12] | [7, 12] | 12 | 2 | 7 > 6? YES. Shoot at 12 (end) |
| 4 | [10, 16] | [10, 16] | 12 | 2 | 10 <= 12? YES (Burst!) |
(1)-------(6) <- Arrow 1 at 6
(2)-----------(8)
(7)-------(12) <- Arrow 2 at 12
(10)--------(16)Non-overlapping Intervals
Given an array of intervals intervals where intervals[i] = [start_i, end_i], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Intervals: [[1,2], [2,3], [3,4], [1,3]]
1--2
2--3
3--4
1-----3
To keep most intervals (3), we must remove [1,3].
Result: 1Examples
[1,3] can be removed and the rest of intervals are non-overlapping.
Intuition
The problem asks for the minimum removals to remove all overlaps. This is equivalent to finding the maximum number of non-overlapping intervals. We can generate all combinations of intervals (take it or leave it) and find the maximum valid set.
start time so we can easily check for overlaps sequentially.currIndex, we have two choices:
prevIndex (i.e., intervals[prevIndex].end <= intervals[currIndex].start). We add 1 to our count and move to currIndex + 1.currIndex + 1 without adding to our count.Detailed Dry Run
intervalsSorted = [[1,2], [1,3], [2,3]]
Intuition
This problem is equivalent to finding the Longest Chain of Pairs or the Longest Increasing Subsequence. We want to find the maximum number of intervals k that can be kept. Then the result is N - k.
start time.dp array where dp[i] is the maximum number of non-overlapping intervals ending at index i.i, look at all j < i. If intervals[j].end <= intervals[i].start, then we can extend the chain: dp[i] = max(dp[i], dp[j] + 1).Detailed Dry Run
intervalsSorted = [[1,2], [1,3], [2,3]], dp=[1,1,1]
Intuition
To keep the maximum number of intervals, we should always pick the interval that ends the earliest. This is the classic Interval Scheduling problem. Why? Because an earlier end time leaves the most possible room for subsequent intervals.
end point.lastEnd time of the last accepted interval.start >= lastEnd, accept it and update lastEnd.removals count.Detailed Dry Run
intervals = [[1,2], [2,3], [3,4], [1,3]] After Sort: [[1,2], [2,3], [1,3], [3,4]]
| Step | Interval | Range | lastEnd | Action |
|---|---|---|---|---|
| 1 | [1, 2] | [1, 2] | 2 | Keep [1,2]. lastEnd = 2 |
| 2 | [2, 3] | [2, 3] | 3 | 2 >= 2? YES. Keep [2,3]. lastEnd = 3 |
| 3 | [1, 3] | [1, 3] | 3 | 1 < 3? NO. Remove [1,3]. |
| 4 | [3, 4] | [3, 4] | 4 | 3 >= 3? YES. Keep [3,4]. lastEnd = 4 |
Result: 1 removal.
Queue Reconstruction by Height
You are given an array of people, people, which are the attributes of some people in a queue (not necessarily in order). Each people[i] = [h_i, k_i] represents the i-th person of height h_i who has exactly k_i other people in front of them with a height greater than or equal to h_i.
Reconstruct and return the queue that is represented by the input array people.
Input: [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
Step 1: Sort by height DESC, then k index ASC.
[[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
Step 2: Insert into list at index k.
Insert [7,0] at 0: [[7,0]]
Insert [7,1] at 1: [[7,0], [7,1]]
Insert [6,1] at 1: [[7,0], [6,1], [7,1]]
Insert [5,0] at 0: [[5,0], [7,0], [6,1], [7,1]]
...Examples
Result matches the height/k-index constraints.
Intuition
If we sort people by height ascending, we can process each person and find their relative position by counting 'empty slots' in the final array. Each empty slot is reserved for someone taller (or same height) than the current person.
[h, k]:
Detailed Dry Run
people = [[7,0], [4,4], [7,1], [5,0], [5,2], [6,1]] Sorted (H asc, K desc): [[4,4], [5,2], [5,0], [6,1], [7,1], [7,0]]
Intuition
Taller people don't care about shorter people behind them. If we process taller people first, their relative k index in the final queue will be exactly equal to their index in the list.
k.k.Detailed Dry Run
people = [[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
| Step | Person | Action | Queue |
|---|---|---|---|
| 1 | [7,0] | Insert at 0 | [[7,0]] |
| 2 | [7,1] | Insert at 1 | [[7,0], [7,1]] |
| 3 | [6,1] | Insert at 1 | [[7,0], [6,1], [7,1]] |
| 4 | [5,0] | Insert at 0 | [[5,0], [7,0], [6,1], [7,1]] |
| 5 | [5,2] | Insert at 2 | [[5,0], [7,0], [5,2], [6,1], [7,1]] |
| 6 | [4,4] | Insert at 4 | [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] |
Intuition
Finding the -th empty slot in is the bottleneck in the simulation. We can speed this up to using a Binary Indexed Tree (BIT) or Segment Tree by tracking the number of available slots.
Detailed Dry Run
Sorted: [[4,4], [5,2], [5,0], [6,1], [7,1], [7,0]] BIT tracks empty slots (initially all 1s).
Task Scheduler
Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. For each unit of time, the CPU could complete either one task or stay idle.
There is a cooldown period n between two same tasks, meaning at least n units of time must pass between any two executions of the same task.
Return the least number of units of time to finish all tasks.
tasks = ["A","A","A","B","B","B"], n = 2
Timeline:
A -> B -> idle -> A -> B -> idle -> A -> B
1 2 3 4 5 6 7 8
Total Time: 8Examples
A possible sequence: A -> B -> idle -> A -> B -> idle -> A -> B.
Intuition
At each unit of time, we want to pick the most frequent task that is currently available (not in cooldown). We can simulate this step-by-step.
nextAvailableTime for each task.t, pick the available task with the highest remaining frequency.Detailed Dry Run
tasks=[A,A,B], n=2 t=0: best=A (count 2). nextAvail[A]=3, rem=2. t=1: best=B (count 1). nextAvail[B]=4, rem=1. t=2: no one avail (A needs t=3). idle. t=3: best=A (count 1). nextAvail[A]=6, rem=0. Total: 4 units.
Intuition
Instead of scanning all tasks every time, we use a Max-Heap to always pick the highest frequency task. Tasks in cooldown are stored in a temporary wait-queue and added back to the heap once their cooldown expires.
{remaining_freq, release_time} for tasks in cooldown.release_time <= current_time, push it back to the Heap.release_time = current_time + n + 1.Detailed Dry Run
tasks=[A,A,A,B,B,B], n=2 Heap: [(A,3), (B,3)] t=0: Pop (A,3). Add to waitQueue: (A,2) at t=3. time=1. t=1: Pop (B,3). Add to waitQueue: (B,2) at t=4. time=2. t=2: Heap empty. Wait till t=3... time=3. t=3: WaitQueue release (A,2). Heap: [(A,2)]...
Intuition
The total time is bounded by the most frequent task. If 'A' is the most frequent (frequency ), we have blocks. Each of the first blocks must be followed by slots (either other tasks or idle).
max_f be the maximum frequency.(max_f - 1) * (n + 1) slots.max_f frequency will fill one extra slot at the very end.(max_f - 1) * (n + 1) + num_tasks_with_max_f.tasks.length.Detailed Dry Run
tasks = [A,A,A,B,B,B], n = 2
| Variable | Value | Explanation |
|---|---|---|
| maxFreq | 3 | 'A' and 'B' both appear 3 times |
| countMax | 2 | Two tasks (A, B) have max frequency |
| Formula | (3-1)*(2+1) + 2 | 2 * 3 + 2 = 8 |
| Final | max(8, 6) | 8 |
[A, B, _] [A, B, _] [A, B] -> Total 8 slots.
Minimum Number of Taps to Water Garden
There is a one-dimensional garden from 0 to n. There are n + 1 taps at points [0, 1, ..., n]. Tap i can water the area [i - ranges[i], i + ranges[i]].
Return the minimum number of taps to water the entire garden [0, n]. If impossible, return -1.
n = 5, ranges = [3, 4, 1, 1, 0, 0]
Tap 0: [0, 3]
Tap 1: [0, 5] <--- Covers everything alone!
Tap 2: [1, 3]
...
Optimal: 1 (Tap 1)Examples
Tap at index 1 covers [0,5], which is the entire garden.
Intuition
Try every subset of taps and check if they together cover the garden [0, n]. This can be implemented using recursion by deciding for each tap whether to include it or not.
solve(tapIdx, currentCoverage)tapIdx == n + 1, check if currentCoverage >= n.Detailed Dry Run
n=3, taps=[[0,2], [1,3]]
Intuition
Define dp[i] as the minimum number of taps needed to water the garden from index to . For each tap, we update all indices it covers.
dp array of size n+1 with a large value (infinity).dp[0] = 0 (no taps needed to cover index 0).i with range [left, right]:
j such that left <= j <= right:
dp[right] = min(dp[right], dp[j] + 1).Detailed Dry Run
n=5, ranges=[3,4,1,1,0,0], dp=[0, inf, inf, inf, inf, inf]
Intuition
This is equivalent to finding the minimum jumps to reach n. We transform each tap into a 'jump' from its left boundary to its right boundary.
maxReach[left] = max(maxReach[left], right).Detailed Dry Run
n=5, ranges=[3,4,1,1,0,0] MaxReach: [5, 0, 3, 0, 4, 5] (Index 0 can reach 5 via Tap 1)
| i | farthest | end | taps | Action |
|---|---|---|---|---|
| 0 | 5 | 0 | 1 | i == end, taps++, end = 5 |
| 1-4 | 5 | 5 | 1 | farthest doesn't change |
| Result: 1 |
Maximum Length of Pair Chain
You are given an array of n pairs pairs where pairs[i] = [left_i, right_i] and left_i < right_i.
A pair p2 follows p1 if p1[1] < p2[0]. Return the length of the longest chain.
Pairs: [[1,2], [7,8], [4,5]]
1. Sort by end points: [1,2], [4,5], [7,8]
2. Chain: [1,2] -> [4,5] -> [7,8]
Length: 3Examples
Longest chain: [1,2] -> [3,4]. Note: [2,3] cannot follow [1,2] because 2 is not < 2.
Intuition
Try all possible subsequences of pairs and check if they form a valid chain. To optimize slightly, we can first sort by start time and use recursion with a 'pick or skip' strategy.
solve(idx, prevEnd)idx:
pairs[idx].start > prevEnd, we can pick it: 1 + solve(idx + 1, pairs[idx].end).solve(idx + 1, prevEnd).Detailed Dry Run
pairs = [[1,2], [2,3], [3,4]]
Intuition
This problem is perfectly analogous to Longest Increasing Subsequence (LIS). We want to find the longest chain where each pair follows the previous one.
dp array where dp[i] is the length of the longest chain ending at index i (initially all 1).i, check all j < i: if pairs[j].end < pairs[i].start, then dp[i] = max(dp[i], dp[j] + 1).Detailed Dry Run
pairs = [[1,2], [2,3], [3,4]], dp=[1, 1, 1]
Intuition
To fit the maximum number of intervals into a chain, we should always pick the pair that ends the earliest. This leaves the maximum room for subsequent pairs.
pair[1].currEnd of the chain.start > currEnd, add it to the chain and update currEnd.Detailed Dry Run
pairs = [[1,2], [7,8], [4,5]] Sorted by end: [[1,2], [4,5], [7,8]]
| Pair | currEnd | Action |
|---|---|---|
| [1,2] | 2 | Pick [1,2], count=1 |
| [4,5] | 5 | 4 > 2? YES. Pick [4,5], count=2 |
| [7,8] | 8 | 7 > 5? YES. Pick [7,8], count=3 |
Meeting Rooms II
Given an array of meeting time intervals intervals where intervals[i] = [start_i, end_i], return the minimum number of conference rooms required.
Intervals: [[0, 30], [5, 10], [15, 20]]
Room 1: [0--------------------------30]
Room 2: [5---10] [15---20]
Max overlapping at any time: 2
Result: 2Examples
We need two rooms; one for [0, 30] and another for [5, 10] and [15, 20] sequentially.
Intuition
Count the number of overlapping meetings at every possible time point. The maximum count across all time points is the minimum number of rooms needed.
minStart and maxEnd across all intervals.minStart to maxEnd:
start <= t < end).Detailed Dry Run
[[0, 30], [5, 10], [15, 20]]
Intuition
When a meeting starts, we need a room; when it ends, we free a room. If we process all start and end points in chronological order, the max number of overlapping meetings at any point is our answer.
startTime < endTime, a new meeting starts before an old one ends: increment rooms and startPtr.rooms and endPtr.Detailed Dry Run
Starts: [0, 5, 15], Ends: [10, 20, 30]
Intuition
As we process meetings by start time, we use a Min-Heap to track the end times of rooms currently in use. The top of the heap is the room that will be free soonest.
start >= heap.peek() (earliest end), we can reuse that room: heap.pop().heap.size().Detailed Dry Run
[[0,30],[5,10],[15,20]] Sorted
Candy Distribution
Each child has a rating. You must give candies such that:
Return the minimum total candies.
Ratings: [1, 0, 2]
Pass 1 (L->R):
[1, 0, 2] -> [1, 1, 2] (fixes child at index 2 rank > index 1)
Pass 2 (R->L):
[1, 0, 2] -> [2, 1, 2] (fixes child at index 0 rank > index 1)
Total: 5Examples
Candies: [2, 1, 2]. Total = 5.
Intuition
Repeatedly scan the ratings and candies. If a child has a higher rating than a neighbor but doesn't have more candies, give them one more. Repeat until no more changes are needed.
candies with 1 for all.changed = false).i:
ratings[i] > ratings[i-1] and candies[i] <= candies[i-1], set candies[i] = candies[i-1] + 1, changed = true.i+1.changed is false.Detailed Dry Run
ratings=[1,2,3], initial=[1,1,1] Pass 1: [1,2,1] (i=1), [1,2,3] (i=2). changed=true. Pass 2: No changes. Total=6.
Intuition
We can think of the ratings as a sequence of mountains (peaks) and valleys. The number of candies increases as we climb a mountain from a valley, and decreases as we descend. The 'peak' child gets the maximum needed from both sides.
up, down, and peak to track the current mountain slopes.up++, down=0, peak=up. Candies += up + 1.down++, up=0. Candies += down.down > peak, we need to add one extra candy to the peak to keep it higher than the descending slope.Detailed Dry Run
ratings = [1, 2, 1]
Intuition
A child must satisfy two conditions: candies[i] > candies[i-1] if ratings[i] > ratings[i-1], and candies[i] > candies[i+1] if ratings[i] > ratings[i+1]. We can solve these separately.
rating[i] > rating[i-1], set candies[i] = candies[i-1] + 1.rating[i] > rating[i+1], set candies[i] = max(candies[i], candies[i+1] + 1).Detailed Dry Run
ratings = [1, 2, 8, 7, 3, 4, 1] L->R: [1, 2, 3, 1, 1, 2, 1] R->L: [1, 2, 3, 2, 1, 2, 1] Result: 12
Minimum Cost to Connect Sticks
You have sticks with positive integer lengths. You can connect any two sticks into one by paying a cost equal to their sum. Return the minimum cost to connect all sticks.
Sticks: [2, 4, 3]
1. Combine 2 and 3: Cost = 5. Remaining: [5, 4]
2. Combine 5 and 4: Cost = 9. Remaining: [9]
Total: 14Examples
Combining 2+3 first followed by 4 is cheaper than other combinations.
Intuition
Try every possible sequence of connecting two sticks and find the one that results in the minimum total cost. This can be implemented using recursion by trying all pairs at each step.
cost = sticks[i] + sticks[j].Detailed Dry Run
sticks = [2, 4, 3]
Intuition
At each step, we identify the two smallest sticks by sorting the list, combine them, and repeat. While better than brute force, re-sorting at every step is inefficient.
totalCost, remove the two elements, and add the sum back.Detailed Dry Run
sticks = [2, 4, 3]
Intuition
To minimize the total cost, we should always combine the two smallest sticks currently available. This is the Huffman Coding principle.
heap.size() > 1:
s1 and s2 (two smallest).cost = s1 + s2.totalCost += cost, Push cost back to heap.Detailed Dry Run
Sticks: [1, 8, 3, 5]
Patching Array
Add minimum elements to a sorted array so all numbers in [1, n] can be formed by sums.
nums = [1, 3], n = 6
Reach: [1, 1] (via 1)
Next miss: 2. nums[i]=3 > 2. PATCH 2.
Reach: [1, 3] (via 1, 2)
Next miss: 4. nums[i]=3 <= 4. Use 3.
Reach: [1, 6] (via 1, 2, 3)
Done. Patches: 1Examples
Adding 2 allows forming sums up to 6.
Intuition
Try all possible combinations of patches (numbers from 1 to n) and check if each subset sum can form all numbers in [1, n]. This is extremely slow but guaranteed to find the minimum patches.
isPossible(current_nums, n): Checks if all numbers in [1, n] can be formed.solve(current_nums, count): Recursively tries adding each possible number p that is not in current_nums.count such that isPossible is true.Detailed Dry Run
nums = [1, 3], n = 6
Intuition
Treat this as a variation of the Subset Sum problem. For a given set of numbers, maintain a boolean array can_form where can_form[i] is true if sum i can be reached.
dp[i] = true if sum i is possible.nums. Update dp for each number.dp[miss] is false for some miss <= n, we must add a patch.Detailed Dry Run
nums = [1, 3], n = 6, dp=[T, F, F, F, F, F, F] (dp[0]=T)
Intuition
Maintain the smallest number miss that cannot be formed. If nums[i] <= miss, use it to extend the range. Otherwise, patch miss to double the reach.
miss = current gap.nums[i] <= miss: miss += nums[i++].miss += miss, count++.Detailed Dry Run
nums = [1, 5, 10], n = 20 miss=1: Use 1, miss=2 miss=2: nums[1]=5 > 2. Patch 2, miss=4 miss=4: nums[1]=5 > 4. Patch 4, miss=8 miss=8: Use 5, miss=13 miss=13: Use 10, miss=23. DONE.
Reorganize String
Rearrange string so no two adjacent characters are identical.
s = "aaabcc"
a: 3, b: 1, c: 2
1. Fill 'a' at 0, 2, 4 -> [a, _, a, _, a, _]
2. Fill 'c' at 1, 3 -> [a, c, a, c, a, _]
3. Fill 'b' at 5 -> [a, c, a, c, a, b]Examples
Interleaving 'a' and 'b' satisfies the requirement.
Intuition
Generate all possible permutations of the input string and check if any of them satisfy the condition that no two adjacent characters are identical.
s[i] == s[i+1].Detailed Dry Run
s = "aab" Permutations: ["aab", "aba", "baa"]
Intuition
Always pick the most frequent character that is different from the last one placed. A Max-Heap allows us to fetch the highest frequency character efficiently.
(count, char).Detailed Dry Run
s = "aaabcc" Heap: [(a,3), (c,2), (b,1)]
Intuition
Place the most frequent character at even indices (0, 2, 4...). If it fits, we can always interleave the rest.
Detailed Dry Run
s = "aab" Max char 'a' count = 2. Place at 0, 2. [a, _, a] Next char 'b' count = 1. Place at 1. [a, b, a]
Minimum Number of K Consecutive Bit Flips
You are given a binary array nums and an integer k. A k-bit flip is choosing a subarray of length k and reversing all bits. Return the minimum flips to make all elements 1.
nums = [0, 1, 0], k = 1
i=0: 0 -> Flip! [1, 1, 0], ans=1
i=1: 1 -> OK.
i=2: 0 -> Flip! [1, 1, 1], ans=2
Total: 2Examples
Flip nums[0], then flip nums[2].
Intuition
Traverse the array from left to right. Whenever we encounter a 0, we flip the next k elements. If at any point we need to flip but don't have enough elements left, it's impossible.
i from 0 to n-1:
nums[i] == 0:
i + k > n. If so, return -1.nums[i...i+k-1].ans.ans.Detailed Dry Run
nums = [0, 1, 0, 1], k = 2
Intuition
Instead of manually flipping elements, use a queue to track which indices still have an active flip effect. This avoids the inner loop.
i, remove expired flips from the queue (indices where ).i is queue.size().nums[i] is effectively 0 (i.e., nums[i] == queue.size() % 2), start a new flip at i.Detailed Dry Run
nums = [0, 0, 0], k = 2 i=0: nums[0]=0, q=[] (size 0). 0==0. Push 0. q=[0], ans=1. i=1: nums[1]=0, q=[0] (size 1). 0!=1. OK. i=2: i-k=0. Expire 0. q=[]. nums[2]=0. 0==0. Push 2. i+k > n. Return -1.
Intuition
Traverse left to right. If nums[i] is 0 after all previous flips affecting it, we MUST flip the window starting at i. Use a queue or difference array to track active flips efficiently.
curFlips = count of active flips affecting index i.i reaches k, remove the effect of the flip that started at i-k.nums[i] is effectively 0 (i.e., nums[i] == curFlips % 2), flip it.Detailed Dry Run
nums = [0,0,0,1,0], k=3 i=0: nums[0]=0, cur=0. Flip! ans=1, cur=1, mark[0]=1 i=1,2: cur=1, nums[i] effectively 1. OK. i=3: i>=3, remove mark[0]. cur=0. nums[3]=1. OK. i=4: nums[4]=0, cur=0. Flip! i+k > n. FAIL.
Weighted Job Scheduling
Find maximum profit from non-overlapping jobs with given startTime, endTime, and profit.
Jobs (S, E, P):
J1: [1, 3, 50], J2: [2, 4, 10], J3: [3, 5, 40], J4: [3, 6, 70]
Combination J1 (50) + J4 (70) = 120 (Max)Examples
The subset of jobs [J1, J4] gives maximum profit.
Intuition
Try all possible subsets of compatible jobs and return the maximum profit. To handle compatibility, we sort by start time and at each step, decide to either pick the current job and skip all overlapping ones, or skip the current job.
solve(idx): Max profit starting from job idx.idx -> solve(idx + 1).idx. Find the next job k > idx that starts after jobs[idx].end. Result = profit[idx] + solve(k).max(Decision 1, Decision 2).Detailed Dry Run
Jobs: [1,3,50], [2,4,10], [3,5,40]
Intuition
This is a variation of the Longest Increasing Subsequence (LIS) problem. For each job i, we look at all previous jobs j < i and if they are compatible, we update dp[i] = max(dp[i], dp[j] + profit[i]).
dp[i] = max profit ending at job i.i, iterate all j < i and check if jobs[j].end <= jobs[i].start.Detailed Dry Run
Jobs: [J1:1-3, 50], [J2:2-4, 10], [J3:3-5, 40]
Intuition
Sort by end time. For each job, the max profit is either excluding it (same as max profit of previous job) or including it (profit + max profit of latest compatible job).
endTime.dp[i] = max profit for first i jobs.j < i where jobs[j].end <= jobs[i].start using binary search.dp[i] = max(dp[i-1], job[i].profit + dp[j]).Detailed Dry Run
Sorted: [J1:[1,3,50], J2:[2,4,10], J3:[3,5,40], J4:[3,6,70]] J1: dp[0]=50 J2: j=-1, dp[1]=max(50, 10)=50 J3: j=0, dp[2]=max(50, 40+50)=90 J4: j=0, dp[3]=max(90, 70+50)=120
Course Schedule III
Take maximum number of courses given [duration, lastDay] constraints.
Courses: [[100, 200], [1000, 1250], [200, 1300]]
1. [100, 200] -> time=100. Heap: [100].
2. [1000, 1250] -> time=1100. Heap: [100, 1000].
3. [200, 1300] -> time=1300. Heap: [100, 200, 1000].
Result: 3Examples
Take course 1, 3, and 2.
Intuition
Try all possible subsets of courses. For each subset, check if there exists an ordering that satisfies all deadlines. The maximum subset size that works is the answer.
Detailed Dry Run
[[100,200], [1000, 1250], [200, 1300]]
Intuition
This can be viewed as an 0/1 Knapsack problem where the 'weight' is the duration and the 'limit' is the deadline. We sort by deadline first to ensure we process courses in an order that respects feasibility.
lastDay.dp[i][t] = max courses among first i that can be finished by time t.dp[i][j] = min time to finish j courses using first i courses.Detailed Dry Run
Sorted: [[100,200], [200, 1300]] dp[0][0]=0, dp[0][1]=100 (others inf) dp[1][0]=0, dp[1][1]=min(100, 200)=100, dp[1][2]=100+200=300 (300<=1300? Yes)
Intuition
Process courses by deadline. If a course doesn't fit, replace the longest course already taken with this shorter one to save time.
lastDay.time and a Max-Heap of durations.time and heap.time > lastDay, subtract the max duration from time (pop heap).Detailed Dry Run
[[100, 200], [500, 600], [200, 600]]
Two City Scheduling
A company is planning to interview 2n people. Given the array costs where costs[i] = [aCost_i, bCost_i], the cost of flying the person to city A is aCost_i, and the cost of flying the person to city B is bCost_i.
Return the minimum cost to fly every person to a city such that exactly n people arrive in each city.
Costs: [[10, 20], [30, 200], [400, 50], [30, 20]]
Profit of picking A over B (a - b):
1. 10 - 20 = -10
2. 30 - 200 = -170 (Huge saving if A)
3. 400 - 50 = +350 (Huge saving if B)
4. 30 - 20 = +10
Sorted diffs: -170, -10, +10, +350
Pick first 2 for A, last 2 for B.Examples
Fly person 1 and 2 to city A, and person 3 and 4 to city B.
Intuition
Try every possible way to assign people to City A and people to City B. This results in combinations.
solve(idx, countA, countB): Assign person idx.countA < n).countB < n).Detailed Dry Run
[[10,20], [30,200]]
Intuition
This is a classic 2D DP problem. dp[i][j] represents the minimum cost for the first i+j people, with i people assigned to City A and j people assigned to City B.
dp[i][j] = min cost for i in A and j in B.dp[i][j] = min(dp[i-1][j] + costA, dp[i][j-1] + costB).dp[0][0] = 0.Detailed Dry Run
[[10,20], [30,200], [400,50], [30,20]] dp[1][0] = 10, dp[0][1] = 20 dp[1][1] = min(10+200, 20+30) = 50 dp[2][0] = 10+30 = 40 (N=2) ...
Intuition
Imagine we send everyone to City B first. Now we need to pick people to 'swap' to City A. To minimize total cost, we should pick the people for whom the cost reduction (or least increase) of moving to A is greatest. This is .
Detailed Dry Run
Costs: [[10,20], [30,200], [400,50], [30,20]] Diffs (A-B): [-10, -170, 350, 10] Sorted by diff: [-170, -10, 10, 350] People: [30,200], [10,20] -> Go to A. [30,20], [400,50] -> Go to B. Total: 30 + 10 + 20 + 50 = 110.
Hand of Straights
Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards.
Given an integer array hand and an integer groupSize, return true if she can rearrange the cards, or false otherwise.
Hand: [1,2,3,6,2,3,4,7,8], Size: 3
1. Smallest is 1. Start group: [1, 2, 3]. Remaining: [6, 2, 3, 4, 7, 8]
2. Smallest is 2. Start group: [2, 3, 4]. Remaining: [6, 7, 8]
3. Smallest is 6. Start group: [6, 7, 8]. Remaining: []
Result: TrueExamples
Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8].
Intuition
Try all possible ways to group the cards. For each card, either start a new group or add it to an existing incomplete group. This is highly inefficient but follows the core requirement.
solve(remaining_cards): Pick a card and try to form a group of groupSize starting with it.true.Detailed Dry Run
Hand: [1,2,3,4], Size: 2
Intuition
Sort the hand first. Then, repeatedly find the smallest card and try to form a group of size groupSize starting from it by searching for subsequent cards in the array.
hand.used to mark cards.i from 0 to n-1:
!used[i]:
used[i] = true. This is the start of a group.hand[i]+1, hand[i]+2, ..., hand[i]+groupSize-1 in the remaining array.false.Detailed Dry Run
Hand: [1,2,2,3,3,4], Size: 3. Sorted: [1,2,2,3,3,4]
Intuition
To form consecutive groups, every 'start' of a group must be the smallest available card. If we always start groups with the smallest remaining card, we either succeed or hit a card that cannot complete a group.
count[C] groups.count[C], return false.Detailed Dry Run
Hand: [1,2,2,3,3,4], Size: 3 Map: {1:1, 2:2, 3:2, 4:1}
Minimum Initial Energy to Finish Tasks
You are given an array tasks where tasks[i] = [actual_i, minimum_i]:
actual_i is the actual amount of energy you spend to finish the task.minimum_i is the minimum amount of energy you need to begin the task.You can finish the tasks in any order. Return the minimum initial amount of energy you need to finish all tasks.
Tasks: [[1, 3], [2, 4], [10, 11]]
Optimal Order: Sort by (minimum - actual) descending.
1. [1, 3]: diff 2
2. [2, 4]: diff 2
3. [10, 11]: diff 1
Simulation starting with 14:
[1, 3]: 14 >= 3. Spend 1. Left 13.
[2, 4]: 13 >= 4. Spend 2. Left 11.
[10,11]: 11 >= 11. Spend 10. Left 1.
Result: 14Examples
Starting with 14 energy: finish [1,3] (left 13), [2,4] (left 11), [10,11] (left 1).
Intuition
Since we can finish tasks in any order, we can try all permutations of tasks. For each permutation, we calculate the minimum initial energy required to complete the tasks in that specific order. The answer is the minimum of these values.
energy = max(min_i, energy + actual_i).energy found across all permutations.Intuition
If we can finish the tasks with initial energy E, then we can also finish them with any energy E' > E. This monotonicity allows us to binary search for the minimum energy. However, even with binary search, we still need a greedy order to verify feasibility efficiently.
[sum(actual), 10^9].canFinish(energy): Try a greedy order (e.g., sort by minimum - actual desc) and see if the tasks are completed.canFinish(mid) is true, try smaller; else try larger.Detailed Dry Run
Try Energy 14 for [[1,3],[2,4],[10,11]]
Intuition
To minimize initial energy, we want to perform tasks that 'save' the most energy requirement for later. The 'saved' energy after a task is . Sorting tasks by this difference in descending order ensures we handle the most restrictive tasks (relative to their cost) while we have the most energy.
(minimum - actual) descending.0 energy. For each task, you must have at least m energy, so energy = max(m, energy + a).totalEnergy needed and currentEnergy available.Detailed Dry Run
tasks = [[1,3],[2,4],[10,11]] Sorted by (m-a) desc: [1,3] (2), [2,4] (2), [10,11] (1)
| Task | Actual | Minimal | New Energy Needed |
|---|---|---|---|
| [10,11] | 10 | 11 | max(11, 0+10) = 11 |
| [2,4] | 2 | 4 | max(4, 11+2) = 13 |
| [1,3] | 1 | 3 | max(3, 13+1) = 14 |
Maximum Number of Events That Can Be Attended
You are given an array of events where events[i] = [startDay_i, endDay_i]. Every event i starts at startDay_i and ends at endDay_i. You can attend an event i at any day d where startDay_i <= d <= endDay_i.
You can only attend one event at any day d.
Return the maximum number of events you can attend.
Events: [[1, 2], [1, 2], [3, 3]]
Day 1: Attend [1, 2] (Event 1)
Day 2: Attend [1, 2] (Event 2)
Day 3: Attend [3, 3] (Event 3)
Result: 3Examples
You can attend all 3 events.
Intuition
Try every possible combination of events you could attend. For each subset of events, check if there exists a valid day for each event such that no two events are attended on the same day and each event is attended within its [start, end] range.
Intuition
For each day, we scan all available events that have not been attended yet and pick the one that ends the earliest. This is a direct simulation of the optimal strategy without a Priority Queue.
startDay.used boolean array.d from minStart to maxEnd:
i such that events[i].start <= d and events[i].end >= d and it has the minimum end time among all such candidates.used[i] = true and increment count.Detailed Dry Run
Events: [[1,2],[1,2],[3,3]] Day 1: Both [1,2] available. Pick one. Mark used. Day 2: Only other [1,2] available. Pick it. Mark used. Day 3: [3,3] available. Pick it. Mark used. Total: 3.
Intuition
On any given day, if multiple events are available, we should prioritize the one that ends the earliest. This is because an event with a later end day can potentially be attended on a future day.
startDay.Detailed Dry Run
events = [[1,2], [1,2], [3,3]]
| Day | Add to Heap | Expired | Attend | Heap |
|---|---|---|---|---|
| 1 | [2, 2] | - | Attend(2) | [2] |
| 2 | - | - | Attend(2) | [] |
| 3 | [3] | - | Attend(3) | [] |
| Result: 3 |
Help us improve! Report bugs or suggest new features on our Telegram group.