Greedy
Expert Answer & Key Takeaways
Jump Game II
nums of length n. You are initially positioned at nums[0].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 < n
nums[n - 1]. The test cases are generated such that you can reach nums[n - 1].Visual Representation
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
Level I: Dynamic Programming (Bottom-Up)
Intuition
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.Thought Process
dp[i]is the min jumps from indexiton-1.- Base case:
dp[n-1] = 0. - Recurrence:
dp[i] = 1 + min(dp[j])for alli < j <= i + nums[i]. - Initialize
dpwith infinity.
Pattern: DAG Shortest Path
Detailed Dry Run
| 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 |
Level II: Breadth-First Search (Queue-Based)
Intuition
Thought Process
- Queue stores
(index, levels). - Use
visitedset to avoid cycles/redundancy. - For each popped index, push all unvisited neighbors .
Pattern: Level-Order Traversal
Detailed Dry Run
Level III: Optimal Greedy (Window-Based BFS)
Intuition
Thought Process
jumps,curEnd,farthestvariables.- Iterate through array (excluding last index).
- Update
farthest = max(farthest, i + nums[i]). - If
i == curEnd, updatecurEnd = farthest,jumps++.
Pattern: Greedy Reachability
Detailed Dry Run
Gas Station
n gas stations along a circular route, where the amount of gas at the station is gas[i].cost[i] of gas to travel from the station to its next station.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.Circular Logic
Examples
Level I: Brute Force (Simulation)
Intuition
Visual Representation
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 -> S2Thought Process
- Iterate
startfrom to . - For each
start, initializetank = 0. - Iterate
ifrom to to visit all stations using(start + i) % n. - If
tank < 0at any point, stop and try nextstart.
Pattern: Circular Simulation
Detailed Dry Run
Level II: Greedy with Total Sum Check
Intuition
Thought Process
- Calculate
totalGasandtotalCost. - If
totalGas < totalCost, return -1. - Otherwise, use the greedy property: if you fail to reach station starting from , any station between and will also fail to reach .
Pattern: Pruning / Feasibility Check
Detailed Dry Run
Level III: Optimal Greedy (One-Pass)
Intuition
totalDiff as we go. If totalDiff is negative at the end, return -1. Otherwise, the last valid start candidate is guaranteed to work.Visual Representation
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.Thought Process
- Use
totalTankto keep running sum of all(gas[i] - cost[i]). - Use
currentTankto track gas from currentstartcandidate. - If
currentTankdrops below 0, the currentstart(and all stations between it andi) is invalid. ResetcurrentTankand setstart = i + 1. - Final answer:
totalTank >= 0 ? start : -1.
Pattern: Candidate Elimination
Detailed Dry Run
| 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
s. We want to partition the string into as many parts as possible so that each letter appears in at most one part.Visual Representation
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
Level I: Dynamic Search
Intuition
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.Thought Process
- Iterate
ifrom 0 to . - Start a new partition:
start = i,end = lastOccurrence(s[i]). - Loop
jfromitoend:end = max(end, lastOccurrence(s[j])).
- When
jreachesend, the partition is complete. Addend - start + 1to result. - Move
itoend + 1.
Pattern: Window Expansion
Detailed Dry Run
- i=0 ('a'), end=2 ('a' at index 2). Partition: [0, 2]
- Check inner: j=1 ('b'), last 'b' is 5. Update end=5.
- Check inner: j=3 ('c'), last 'c' is 4. end stays 5.
- j reaches 5. Partition size = 5-0+1 = 6.
Level II: Merge Intervals
Intuition
Thought Process
- Find
firstandlastoccurrence for each character 'a'-'z'. - Create a list of intervals
[first, last]for characters that appear in the string. - Sort intervals by start time.
- Merge overlapping intervals.
- The size of each merged interval is a partition length.
Pattern: Interval Merging
Detailed Dry Run
- Sorted: [0,2], [1,5], [3,4]
- Merge [0,2] and [1,5] -> [0,5] (overlaps)
- Merge [0,5] and [3,4] -> [0,5] (overlaps) Result: [6]
Level III: Optimal Greedy (One-Pass)
Intuition
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.Thought Process
- Store the
lastIndexfor every character in a map/array. - Maintain
startandendpointers. - Iterate
ifrom 0 to :end = max(end, lastIndex[s[i]]).- If
i == end:- Found partition! Size =
i - start + 1. - Update
start = i + 1.
- Found partition! Size =
Pattern: Greedy Boundary Tracking
Detailed Dry Run
| 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
points where points[i] = [x_start, x_end] denotes a balloon whose horizontal diameter stretches between x_start and x_end.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.Visual Representation
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
Level I: Greedy Simulation (Brute Force)
Intuition
Thought Process
- Maintain a list of remaining balloons.
- While the list is not empty, iterate through all start and end points of the remaining balloons.
- For each point, count how many balloons it intersects. Find the point that yields the maximum count.
- Increment
arrows, filter out the burst balloons, and repeat.
Detailed Dry Run
- Points: 1,6,2,8,7,12. Count 6: bursts [1,6],[2,8] (2).
- Max burst is 2 at x=6. arrows=1. Remaining: [[7,12]]
- Repeat for [[7,12]]: best point 12. arrows=2. Empty.
Level II: Greedy (Sort by Start & Find Overlap)
Intuition
Thought Process
- Sort by
start point. - Pick the first balloon as the initial intersection
[currentStart, currentEnd]. - For each subsequent balloon
[bStart, bEnd]:- If
bStart <= currentEnd(they overlap):- Narrow the intersection:
currentEnd = min(currentEnd, bEnd).
- Narrow the intersection:
- Else (no overlap with current intersection):
- Shoot an arrow!
- Update
currentEnd = bEndand incrementarrows.
- If
Pattern: Intersection Tracking
Detailed Dry Run
- p[0]=[1,6]. currEnd=6, arrows=1.
- p[1]=[2,8]. 2 <= 6 (overlap). currEnd = min(6, 8) = 6.
- p[2]=[7,12]. 7 > 6 (no overlap). arrows=2, currEnd=12.
Level III: Optimal Greedy (Sort by End)
Intuition
start <= current_arrow_pos), it is burst. Otherwise, we need a new arrow at the new balloon's end point.Why Sort by End?
Pattern: Interval Overlap
Detailed Dry Run
| 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!) |
Visualization:
(1)-------(6) <- Arrow 1 at 6
(2)-----------(8)
(7)-------(12) <- Arrow 2 at 12
(10)--------(16)Non-overlapping 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.Visual Representation
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
Level I: Recursive Selection (Brute Force)
Intuition
Thought Process
- Sort the intervals by
starttime so we can easily check for overlaps sequentially. - For each interval at
currIndex, we have two choices:- Include it: Only possible if it doesn't overlap with the last included interval
prevIndex(i.e.,intervals[prevIndex].end <= intervals[currIndex].start). We add 1 to our count and move tocurrIndex + 1. - Exclude it: We move to
currIndex + 1without adding to our count.
- Include it: Only possible if it doesn't overlap with the last included interval
- Return the maximum of these two choices for all steps.
Detailed Dry Run
- solve(-1, 0):
- Take [1,2]: 1 + solve(0, 1) -> 1 + 1 (takes [2,3]) = 2
- Leave [1,2]: solve(-1, 1) -> max(take[1,3], leave[1,3]) = 1 Max kept = 2. Removals = 3 - 2 = 1.
Level II: Dynamic Programming (LIS Variation)
Intuition
k that can be kept. Then the result is N - k.Thought Process
- Sort the intervals by
starttime. - Create a
dparray wheredp[i]is the maximum number of non-overlapping intervals ending at indexi. - For each
i, look at allj < i. Ifintervals[j].end <= intervals[i].start, then we can extend the chain:dp[i] = max(dp[i], dp[j] + 1).
Pattern: Dynamic Programming / LIS
Detailed Dry Run
- i=1 [1,3]: j=0 [1,2] overlaps. dp[1]=1.
- i=2 [2,3]: j=0 [1,2] no overlap. dp[2]=max(1, dp[0]+1)=2.
- i=2 [2,3]: j=1 [1,3] overlaps. dp[2]=2. Max kept = 2. Removals = 3 - 2 = 1.
Level III: Optimal Greedy (Sort by End)
Intuition
Thought Process
- Sort by
endpoint. - Keep track of the
lastEndtime of the last accepted interval. - For each interval:
- If
start >= lastEnd, accept it and updatelastEnd. - Else, it overlaps; increment
removalscount.
- If
Pattern: Interval Scheduling
Detailed Dry Run
| 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 |
Queue Reconstruction by Height
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.people.Visual Representation
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
Level I: Brute Force Simulation (Find Empty Slots)
Intuition
Thought Process
- Sort by height ASC, then DESC for same height.
- Create an empty result array of size .
- For each person
[h, k]:- We need people in front of us who are .
- Find the -th empty slot in the result array and place the person there.
Detailed Dry Run
- [4,4]: Put in 5th empty slot (idx 4). res=[_ _ _ _ [4,4] _]
- [5,2]: Put in 3rd empty slot (idx 2). res=[_ _ [5,2] _ [4,4] _]
- [5,0]: Put in 1st empty slot (idx 0). res=[[5,0] _ [5,2] _ [4,4] _]
- [6,1]: Put in 2nd empty slot (idx 3). res=[[5,0] _ [5,2] [6,1] [4,4] _]
Level II: Optimal Greedy (Sort and Insert)
Intuition
k index in the final queue will be exactly equal to their index in the list.Thought Process
- Sort DESC by height, then ASC by
k. - Insert each person into a dynamic list at index
k. - Shorter people inserted later won't change the count of taller people in front of existing elements.
Pattern: Greedy Insertion
Detailed Dry Run
| 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]] |
Level III: Logarithmic Optimal (Binary Indexed Tree)
Intuition
Thought Process
- Use the height ASC sort from Level I.
- Imagine an array where 1 means 'empty' and 0 means 'filled'.
- We want a position where .
- We can find this using Binary Search over the BIT in or walking the Segment Tree in .
Detailed Dry Run
- [4,4]: Find k=4. BIT query/search gives pos 5. res[4]=[4,4]. BIT update(5, -1).
- [5,2]: Find k=2. BIT query search gives pos 3. res[2]=[5,2]. BIT update(3, -1).
Task Scheduler
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.n between two same tasks, meaning at least n units of time must pass between any two executions of the same task.Visual Representation
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
Level I: Simple Simulation
Intuition
Thought Process
- Count frequencies of all tasks.
- Maintain an array
nextAvailableTimefor each task. - At each time
t, pick the available task with the highest remaining frequency. - If no task is available, stay idle and increment time.
Detailed Dry Run
Level II: Greedy with Max-Heap
Intuition
Thought Process
- Push all task frequencies into a Max-Heap.
- Use a Queue to store
{remaining_freq, release_time}for tasks in cooldown. - At each unit of time:
- Check the Queue: If a task's
release_time <= current_time, push it back to the Heap. - Pop the highest frequency task from the Heap, decrement its frequency, and if it's still > 0, add it to the wait-queue with
release_time = current_time + n + 1.
- Check the Queue: If a task's
Pattern: Priority Queue Simulation
Detailed Dry Run
Level III: Optimal Frequency Analysis (Math)
Intuition
Thought Process
- Let
max_fbe the maximum frequency. - The base structure is
(max_f - 1) * (n + 1)slots. - Any other tasks with the same
max_ffrequency will fill one extra slot at the very end. - Total time =
(max_f - 1) * (n + 1) + num_tasks_with_max_f. - If this result is smaller than the total number of tasks, it means no idle slots are needed, so the answer is
tasks.length.
Pattern: Math / Frequency Analysis
Detailed Dry Run
| 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 |
Visualization:
Minimum Number of Taps to Water Garden
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]].[0, n]. If impossible, return -1.Visual Representation
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
Level I: Brute Force (Recursive Backtracking)
Intuition
Thought Process
solve(tapIdx, currentCoverage)- At each step, either take the current tap or skip it.
- Base case: If
tapIdx == n + 1, check ifcurrentCoverage >= n. - This approach is extremely slow due to combinations.
Detailed Dry Run
- solve(0, 0): Take [0,2] -> solve(1, 2) -> Take [1,3] -> solve(2, 3) -> SUCCESS (2 taps)
- solve(0, 0): Skip [0,2] -> solve(1, 0) -> Take [1,3] -> FAIL (Gap at [0,1])
Level II: Dynamic Programming
Intuition
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.Thought Process
- Initialize
dparray of sizen+1with a large value (infinity). dp[0] = 0(no taps needed to cover index 0).- For each tap
iwith range[left, right]:- For every
jsuch thatleft <= j <= right:dp[right] = min(dp[right], dp[j] + 1).
- For every
Pattern: Interval DP / Shortest Path in DAG
Detailed Dry Run
- Tap 0: [0,3]. dp[1..3] = min(inf, dp[0]+1) = 1. dp=[0, 1, 1, 1, inf, inf]
- Tap 1: [0,5]. dp[1..5] = min(cur, dp[0..1]+1) = 1. dp=[0, 1, 1, 1, 1, 1]
Level III: Optimal Greedy (Jump Game II Variation)
Intuition
n. We transform each tap into a 'jump' from its left boundary to its right boundary.Thought Process
- Preprocess: For each start point , find the maximum end point it can reach:
maxReach[left] = max(maxReach[left], right). - Now it's Jump Game II: Iterate from to , keeping track of the current coverage end and the farthest reach possible.
Pattern: Interval Coverage / Jump Game II
Detailed Dry Run
| 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
n pairs pairs where pairs[i] = [left_i, right_i] and left_i < right_i.p2 follows p1 if p1[1] < p2[0]. Return the length of the longest chain.Visual Representation
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
Level I: Brute Force (Recursive Backtracking)
Intuition
Thought Process
solve(idx, prevEnd)- For each pair
idx:- If
pairs[idx].start > prevEnd, we can pick it:1 + solve(idx + 1, pairs[idx].end). - We can always skip it:
solve(idx + 1, prevEnd).
- If
- Return the maximum of both.
Detailed Dry Run
- solve(0, -inf): Pick [1,2] -> solve(1, 2) -> Pick [3,4] -> SUCCESS (2 pairs)
- solve(0, -inf): Skip [1,2] -> solve(1, -inf) -> Pick [2,3] -> solve(2, 3) -> Pick [3,4] (FAIL: 3 not > 3)
Level II: Dynamic Programming (LIS Variation)
Intuition
Thought Process
- Sort the pairs by their start element.
- Initialize a
dparray wheredp[i]is the length of the longest chain ending at indexi(initially all 1). - For each
i, check allj < i: ifpairs[j].end < pairs[i].start, thendp[i] = max(dp[i], dp[j] + 1).
Pattern: Dynamic Programming / LIS
Detailed Dry Run
- i=1 ([2,3]): j=0 ([1,2]). 2 is not > 2. dp[1]=1.
- i=2 ([3,4]): j=0 ([1,2]). 3 > 2. dp[2]=max(1, dp[0]+1)=2.
- i=2 ([3,4]): j=1 ([2,3]). 3 is not > 3. dp[2]=2. Max dp = 2.
Level III: Optimal Greedy (Interval Scheduling-Style)
Intuition
Thought Process
- Sort pairs by their end element
pair[1]. - Maintain
currEndof the chain. - For each pair, if its
start > currEnd, add it to the chain and updatecurrEnd.
Pattern: Interval Selection / Activity Selection
Detailed Dry Run
| 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
intervals where intervals[i] = [start_i, end_i], return the minimum number of conference rooms required.Visual Representation
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
Level I: Brute Force (Iterate All Time Points)
Intuition
Thought Process
- Find the
minStartandmaxEndacross all intervals. - For every time from
minStarttomaxEnd:- Count how many meetings are active at time (i.e.,
start <= t < end).
- Count how many meetings are active at time (i.e.,
- Return the maximum count found.
Detailed Dry Run
- t=0: active=[[0,30]]. Count=1.
- t=5: active=[[0,30], [5,10]]. Count=2.
- t=15: active=[[0,30], [15,20]]. Count=2. Max count = 2.
Level II: Chronological Sorting (Sweep Line)
Intuition
Thought Process
- Separate starts and ends into two sorted arrays.
- Use two pointers to iterate through them.
- If
startTime < endTime, a new meeting starts before an old one ends: incrementroomsandstartPtr. - Else, a meeting ends: decrement
roomsandendPtr.
Pattern: Sweep Line / Two Pointers
Detailed Dry Run
- t=0: Start [0,30]. rooms=1. max=1.
- t=5: Start [5,10]. rooms=2. max=2.
- t=10: End [5,10]. rooms=1.
- t=15: Start [15,20]. rooms=2. max=2.
Level III: Optimal Greedy (Min-Heap)
Intuition
Thought Process
- Sort meetings by start time.
- Add the first meeting's end time to a Min-Heap.
- For each next meeting, if its
start >= heap.peek()(earliest end), we can reuse that room:heap.pop(). - Always push current meeting's end time to the heap.
- Answer is
heap.size().
Pattern: Resource Allocation / Priority Queue
Detailed Dry Run
- [0,30]: Heap=[30], rooms=1
- [5,10]: 5 < 30? YES. Heap=[10, 30], rooms=2
- [15,20]: 15 >= 10? YES. Pop 10, Push 20. Heap=[20, 30], rooms=2
Candy Distribution
- Every child gets at least 1 candy.
- Children with a higher rating get more candies than their neighbors.
Visual Representation
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
Level I: Brute Force (Iterative Satisfaction)
Intuition
Thought Process
- Initialize
candieswith 1 for all. - In a loop, keep track if any changes were made (
changed = false). - For each child
i:- If
ratings[i] > ratings[i-1]andcandies[i] <= candies[i-1], setcandies[i] = candies[i-1] + 1, changed = true. - Same for
i+1.
- If
- Stop when
changedis false.
Detailed Dry Run
Level II: Peak and Valley (Single Pass)
Intuition
Thought Process
- Use variables
up,down, andpeakto track the current mountain slopes. - If rating increases,
up++,down=0,peak=up. Candies +=up + 1. - If rating decreases,
down++,up=0. Candies +=down. - If
down > peak, we need to add one extra candy to the peak to keep it higher than the descending slope.
Pattern: Slope Analysis
Detailed Dry Run
- i=1 (up): up=1, peak=1, candies = 1 + (1+1) = 3.
- i=2 (down): down=1, up=0. down <= peak (1 <= 1). candies = 3 + 1 = 4. Sum = 4? Wait, [1, 2, 1] should be [1, 2, 1] -> 4. Correct.
Level III: Optimal Greedy (Two Independent Passes)
Intuition
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.Thought Process
- Start all children with 1 candy.
- L-to-R: If
rating[i] > rating[i-1], setcandies[i] = candies[i-1] + 1. - R-to-L: If
rating[i] > rating[i+1], setcandies[i] = max(candies[i], candies[i+1] + 1).
Pattern: Constraint Satisfaction in Multiple Passes
Detailed Dry Run
Minimum Cost to Connect Sticks
Visual Representation
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
Level I: Brute Force (Try All Pairs)
Intuition
Thought Process
- If only 1 stick remains, return 0.
- For every pair of sticks :
- Combine them:
cost = sticks[i] + sticks[j]. - Recursively solve for the new set of sticks.
- Track the minimum total cost across all possible initial pairs.
- Combine them:
Detailed Dry Run
- Option 1: (2,4) -> [6,3]. Then (6,3) -> [9]. Cost = 6 + 9 = 15.
- Option 2: (2,3) -> [5,4]. Then (5,4) -> [9]. Cost = 5 + 9 = 14. (MIN)
- Option 3: (4,3) -> [7,2]. Then (7,2) -> [9]. Cost = 7 + 9 = 16.
Level II: Sorting and Re-sorting
Intuition
Thought Process
- While more than 1 stick exists:
- Sort the current list of sticks.
- Take the two smallest elements, sum them.
- Add the sum to
totalCost, remove the two elements, and add the sum back.
- Repeat until 1 stick remains.
Detailed Dry Run
- Sort: [2, 3, 4]. Combine 2+3=5. Total=5. Sticks: [5, 4]
- Sort: [4, 5]. Combine 4+5=9. Total=14. Sticks: [9] Result: 14
Level III: Optimal Greedy (Min-Heap)
Intuition
Thought Process
- Add all sticks to a Min-Heap.
- While
heap.size() > 1:- Pop
s1ands2(two smallest). cost = s1 + s2.totalCost += cost, Pushcostback to heap.
- Pop
Pattern: Huffman Coding (Greedy Merging)
Detailed Dry Run
- Pop 1, 3. Sum = 4. Total = 4. Heap: [4, 5, 8]
- Pop 4, 5. Sum = 9. Total = 13. Heap: [8, 9]
- Pop 8, 9. Sum = 17. Total = 30. Result: 30
Patching Array
[1, n] can be formed by sums.Visual Representation
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
Level I: Brute Force (Recursion / Subsets)
Intuition
Thought Process
isPossible(current_nums, n): Checks if all numbers in [1, n] can be formed.solve(current_nums, count): Recursively tries adding each possible numberpthat is not incurrent_nums.- Return the minimum
countsuch thatisPossibleis true.
Detailed Dry Run
- isPossible([1, 3], 6)? No (cannot form 2).
- Try adding 2: [1, 2, 3]. sums: 1, 2, 3, 4(1+3), 5(2+3), 6(1+2+3). YES. Min patches = 1.
Level II: Dynamic Programming (Boolean Knapsack)
Intuition
can_form where can_form[i] is true if sum i can be reached.Thought Process
dp[i]= true if sumiis possible.- Start with original
nums. Updatedpfor each number. - If
dp[miss]is false for somemiss <= n, we must add a patch. - Greedy choice for patch is always the smallest missing number to maximize coverage.
Detailed Dry Run
- Use 1: dp=[T, T, F, F, F, F, F]
- Use 3: dp=[T, T, F, T, T, F, F]
- Miss = 2. Patch 2.
- Use 2: dp=[T, T, T, T, T, T, T]. DONE.
Level III: Optimal Greedy (Reachability Range)
Intuition
miss that cannot be formed. If nums[i] <= miss, use it to extend the range. Otherwise, patch miss to double the reach.Thought Process
miss= current gap.- If
nums[i] <= miss:miss += nums[i++]. - Else:
miss += miss,count++.
Pattern: Greedy Range Extension
Detailed Dry Run
Reorganize String
Visual Representation
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
Level I: Brute Force (All Permutations)
Intuition
Thought Process
- Use recursion to generate all permutations.
- For each unique permutation, check adjacency:
s[i] == s[i+1]. - Return the first valid permutation found, or "" if none exist.
Detailed Dry Run
- "aab": a == a (FAIL)
- "aba": a != b, b != a (SUCCESS). Return "aba".
Level II: Greedy with Max-Heap
Intuition
Thought Process
- Count frequencies and push into a Max-Heap
(count, char). - In each step, pop the top character.
- Add it to the result and keep it aside (don't push back yet) because we can't use it in the very next step.
- Push the character from the previous step back into the heap if it still has remaining count.
Pattern: Priority Queue / Interleaving
Detailed Dry Run
- Pop (a,3). Res: "a". prev=(a,2)
- Pop (c,2). Res: "ac". Push prev (a,2). prev=(c,1)
- Pop (a,2). Res: "aca". Push prev (c,1). prev=(a,1) Result: "acacab"
Level III: Optimal Greedy (Frequency Interleaving)
Intuition
Thought Process
- Count frequencies. If any char count , impossible.
- Start with the most frequent char.
- Fill indices 0, 2, 4... then wrap to 1, 3, 5...
Pattern: Frequency-Based Positioning
Detailed Dry Run
Minimum Number of K Consecutive Bit Flips
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.Visual Representation
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
Level I: Brute Force Simulation
Intuition
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.Thought Process
- For each index
ifrom0ton-1:- If
nums[i] == 0:- Check if
i + k > n. If so, return -1. - Manually flip
nums[i...i+k-1]. - Increment
ans.
- Check if
- If
- Return
ans.
Detailed Dry Run
- i=0: nums[0]=0. Flip [0,1]. nums becomes [1, 0, 0, 1]. ans=1.
- i=1: nums[1]=0. Flip [1,2]. nums becomes [1, 1, 1, 1]. ans=2.
- i=2,3: nums[i]=1. OK. Result: 2
Level II: Greedy with Queue
Intuition
Thought Process
- Maintain a queue of indices where a flip was initiated.
- At each index
i, remove expired flips from the queue (indices where ). - The number of flips currently affecting
iisqueue.size(). - If
nums[i]is effectively0(i.e.,nums[i] == queue.size() % 2), start a new flip ati.
Detailed Dry Run
Level III: Optimal Greedy (Sliding Window Flips)
Intuition
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.Thought Process
curFlips= count of active flips affecting indexi.- When index
ireachesk, remove the effect of the flip that started ati-k. - If
nums[i]is effectively0(i.e.,nums[i] == curFlips % 2), flip it.
Pattern: Greedy Sliding Window Flip
Detailed Dry Run
Weighted Job Scheduling
startTime, endTime, and profit.Visual Representation
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
Level I: Recursive Backtracking
Intuition
Thought Process
solve(idx): Max profit starting from jobidx.- Decision 1: Skip job
idx->solve(idx + 1). - Decision 2: Pick job
idx. Find the next jobk > idxthat starts afterjobs[idx].end. Result =profit[idx] + solve(k). - Return
max(Decision 1, Decision 2).
Detailed Dry Run
- solve(0):
- Skip J1: solve(1)
- Pick J1: 50 + solve(2) (J3 starts at 3, J1 ends at 3). Result = 50 + 40 = 90.
- Return 90.
Level II: Dynamic Programming (O(N^2))
Intuition
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]).Thought Process
- Sort jobs by end time.
dp[i]= max profit ending at jobi.- For each
i, iterate allj < iand check ifjobs[j].end <= jobs[i].start. - This approach is and will pass for medium constraints but TLE for .
Detailed Dry Run
- dp[0] = 50 (J1)
- dp[1] = 10 (J2). J1 overlaps with J2.
- dp[2] = 40 + dp[0] = 90. J1 is compatible with J3. Max Profit = 90.
Level III: Optimal (DP + Binary Search)
Intuition
Thought Process
- Combine data and sort by
endTime. dp[i]= max profit for firstijobs.- Search for the latest job
j < iwherejobs[j].end <= jobs[i].startusing binary search. dp[i] = max(dp[i-1], job[i].profit + dp[j]).
Pattern: Dynamic Programming + Binary Search
Detailed Dry Run
Course Schedule III
[duration, lastDay] constraints.Visual Representation
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
Level I: Recursive Backtracking
Intuition
Thought Process
- Generate all subsets of courses.
- For each subset, sort it by deadline and check if it's feasible.
- Return the maximum feasible subset size.
Pattern: Brute Force / Subsets
Detailed Dry Run
- Try subset [[100,200], [200,1300]]: Feasible? 100<=200 (Y), 300<=1300 (Y). Size 2.
- Try subset [[100,200], [1000,1250], [200,1300]]: Feasible? 100<=200, 1100<=1250, 1300<=1300. YES. Size 3.
Level II: Dynamic Programming
Intuition
Thought Process
- Sort courses by
lastDay. dp[i][t]= max courses among firstithat can be finished by timet.- This is still too slow (). A better DP is
dp[i][j]= min time to finishjcourses using firsticourses.
Pattern: Dynamic Programming
Detailed Dry Run
Level III: Optimal Greedy (Max-Heap + Retroactive Swap)
Intuition
Thought Process
- Sort by
lastDay. - Maintain
timeand a Max-Heap of durations. - Add current duration to
timeand heap. - If
time > lastDay, subtract the max duration fromtime(pop heap).
Pattern: Greedy with Retroactive Swap
Detailed Dry Run
- [100, 200]: time=100, heap=[100]
- [500, 600]: time=600, heap=[500, 100]
- [200, 600]: time=800 > 600. Pop 500. time=300, heap=[200, 100] Result: 2
Two City Scheduling
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.n people arrive in each city.Visual Representation
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
Level I: Recursive Backtracking
Intuition
Thought Process
solve(idx, countA, countB): Assign personidx.- Option 1: Assign to A (if
countA < n). - Option 2: Assign to B (if
countB < n). - Return minimum total cost from both paths.
Pattern: Brute Force / Combinations
Detailed Dry Run
- A(10) then B(200) = 210
- B(20) then A(30) = 50 Min = 50.
Level II: Dynamic Programming
Intuition
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.Thought Process
dp[i][j]= min cost foriin A andjin B.- Transition:
dp[i][j] = min(dp[i-1][j] + costA, dp[i][j-1] + costB). - Base case:
dp[0][0] = 0.
Pattern: 2D Dynamic Programming
Detailed Dry Run
Level III: Optimal Greedy (Sorting by Relative Cost)
Intuition
Thought Process
- Assume everyone goes to B initially (Total = ).
- The 'refund' we get by moving person to A instead of B is , or conversely, the 'extra cost' is .
- Sort all people by .
- Pick the first people (those with the most negative or smallest diffs) and send them to A.
Pattern: Greedy Difference Sorting
Detailed Dry Run
Hand of Straights
groupSize, and consists of groupSize consecutive cards.hand and an integer groupSize, return true if she can rearrange the cards, or false otherwise.Visual Representation
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
Level I: Recursive Backtracking
Intuition
Thought Process
solve(remaining_cards): Pick a card and try to form a group ofgroupSizestarting with it.- Recurse with the remaining cards.
- If all cards are used, return
true.
Pattern: Brute Force / Backtracking
Detailed Dry Run
- Try [1,2], left [3,4].
- Try [3,4], left []. Success.
Level II: Sorting + Simulation (O(N^2))
Intuition
groupSize starting from it by searching for subsequent cards in the array.Thought Process
- Sort
hand. - Use a boolean array
usedto mark cards. - For each
ifrom0ton-1:- If
!used[i]:- Mark
used[i] = true. This is the start of a group. - Search for
hand[i]+1, hand[i]+2, ..., hand[i]+groupSize-1in the remaining array. - If any card is not found or already used, return
false.
- Mark
- If
Pattern: Sorting and Linear Scanning
Detailed Dry Run
- Start with 1. Find 2 (idx 1) and 3 (idx 3). Mark used. [X, X, 2, X, 3, 4]
- Next unused is 2. Find 3 (idx 4) and 4 (idx 5). Mark used. [X, X, X, X, X, X] Success.
Level III: Optimal Greedy (TreeMap/Frequency Map)
Intuition
Thought Process
- Count frequencies of all cards.
- Sort unique cards (using a TreeMap or sorted keys).
- For each card that still has remaining counts:
- It MUST be the start of
count[C]groups. - Each such group requires cards .
- If any of these cards have fewer counts than
count[C], returnfalse. - Decrement counts of all cards in the group.
- It MUST be the start of
Pattern: Greedy Frequency Grouping
Detailed Dry Run
- Smallest is 1. Count=1. Start group [1,2,3]. Map becomes: {1:0, 2:1, 3:1, 4:1}
- Next smallest is 2. Count=1. Start group [2,3,4]. Map becomes: {2:0, 3:0, 4:0} Result: True.
Minimum Initial Energy to Finish Tasks
tasks where tasks[i] = [actual_i, minimum_i]:actual_iis the actual amount of energy you spend to finish the task.minimum_iis the minimum amount of energy you need to begin the task.
Visual Representation
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
Level I: Brute Force (All Permutations)
Intuition
Thought Process
- Generate all permutations of tasks.
- For each permutation:
- Start with current energy (working backwards) or try binary search on initial energy.
- Simpler: starting backwards from the last task,
energy = max(min_i, energy + actual_i).
- Return the minimum
energyfound across all permutations.
Pattern: Brute Force / Permutations
Level II: Binary Search on Initial Energy
Intuition
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.Thought Process
- Search range:
[sum(actual), 10^9]. canFinish(energy): Try a greedy order (e.g., sort byminimum - actualdesc) and see if the tasks are completed.- If
canFinish(mid)is true, try smaller; else try larger.
Pattern: Binary Search on Answer
Detailed Dry Run
- Task [1,3]: 14 >= 3. Spend 1. Left 13.
- Task [2,4]: 13 >= 4. Spend 2. Left 11.
- Task [10,11]: 11 >= 11. Spend 10. Left 1. True. Try Energy 13...
Level III: Optimal Greedy (Sorting)
Intuition
Thought Process
- Sort tasks by
(minimum - actual)descending. - Work backwards: Start with
0energy. For each task, you must have at leastmenergy, soenergy = max(m, energy + a). - Alternatively, work forwards: Keep track of
totalEnergyneeded andcurrentEnergyavailable.
Pattern: Greedy Sorting
Detailed Dry Run
| 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
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.d.Visual Representation
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
Level I: Brute Force (Recursive Scheduling)
Intuition
Thought Process
- Use recursion to try and attend or skip each event.
- When attending an event, try every possible day in its range that hasn't been used yet.
- Keep track of the maximum number of events attended.
- This approach is or worse if checking all day combinations.
Pattern: Backtracking / Search
Level II: Greedy Simulation (O(N^2))
Intuition
Thought Process
- Sort events by
startDay. - Maintain a
usedboolean array. - For each day
dfromminStarttomaxEnd:- Find an unvisited event
isuch thatevents[i].start <= dandevents[i].end >= dand it has the minimumendtime among all such candidates. - If found, mark
used[i] = trueand incrementcount.
- Find an unvisited event
Pattern: Greedy Scanning
Detailed Dry Run
Level III: Optimal Greedy (Min-Heap + Sorting)
Intuition
Thought Process
- Sort events by
startDay. - Use a Min-Heap to store the end days of currently available events.
- Iterate through days:
- Add all events starting today to the heap.
- Remove events from the heap that have already ended.
- If the heap is not empty, attend the event that ends the earliest (pop from heap) and increment count.
Pattern: Greedy / Priority Queue
Detailed Dry Run
| Day | Add to Heap | Expired | Attend | Heap |
|---|---|---|---|---|
| 1 | [2, 2] | - | Attend(2) | [2] |
| 2 | - | - | Attend(2) | [] |
| 3 | [3] | - | Attend(3) | [] |
| Result: 3 |
Course4All Technical Board
Verified ExpertSenior Software Engineers & Algorithmic Experts
Our DSA content is authored and reviewed by engineers from top tech firms to ensure optimal time and space complexity analysis.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.