184 lines
8.3 KiB
YAML
184 lines
8.3 KiB
YAML
title: Add Minimum Number of Rungs
|
|
slug: add-minimum-number-of-rungs
|
|
difficulty: medium
|
|
leetcode_id: 1936
|
|
leetcode_url: https://leetcode.com/problems/add-minimum-number-of-rungs/
|
|
categories:
|
|
- arrays
|
|
patterns:
|
|
- slug: greedy
|
|
is_optimal: true
|
|
|
|
function_signature: "def add_rungs(rungs: list[int], dist: int) -> int:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { rungs: [1, 3, 5, 10], dist: 2 }
|
|
expected: 2
|
|
- input: { rungs: [3, 6, 8, 10], dist: 3 }
|
|
expected: 0
|
|
- input: { rungs: [3, 4, 6, 7], dist: 2 }
|
|
expected: 1
|
|
hidden:
|
|
- input: { rungs: [5], dist: 10 }
|
|
expected: 0
|
|
- input: { rungs: [10], dist: 3 }
|
|
expected: 3
|
|
- input: { rungs: [1, 100], dist: 10 }
|
|
expected: 9
|
|
- input: { rungs: [1, 2, 3, 4, 5], dist: 1 }
|
|
expected: 0
|
|
- input: { rungs: [4, 8, 12, 16], dist: 3 }
|
|
expected: 4
|
|
|
|
description: |
|
|
You are given a **strictly increasing** integer array `rungs` that represents the **height** of rungs on a ladder. You are currently on the **floor** at height `0`, and you want to reach the last rung.
|
|
|
|
You are also given an integer `dist`. You can only climb to the next highest rung if the distance between where you are currently at (the floor or on a rung) and the next rung is **at most** `dist`. You are able to insert rungs at any positive **integer** height if a rung is not already there.
|
|
|
|
Return *the **minimum** number of rungs that must be added to the ladder in order for you to climb to the last rung*.
|
|
|
|
constraints: |
|
|
- `1 <= rungs.length <= 10^5`
|
|
- `1 <= rungs[i] <= 10^9`
|
|
- `1 <= dist <= 10^9`
|
|
- `rungs` is **strictly increasing**
|
|
|
|
examples:
|
|
- input: "rungs = [1,3,5,10], dist = 2"
|
|
output: "2"
|
|
explanation: "You currently cannot reach the last rung. Add rungs at heights 7 and 8 to climb this ladder. The ladder will now have rungs at [1,3,5,7,8,10]."
|
|
- input: "rungs = [3,6,8,10], dist = 3"
|
|
output: "0"
|
|
explanation: "This ladder can be climbed without adding additional rungs."
|
|
- input: "rungs = [3,4,6,7], dist = 2"
|
|
output: "1"
|
|
explanation: "You currently cannot reach the first rung from the ground. Add a rung at height 1 to climb this ladder. The ladder will now have rungs at [1,3,4,6,7]."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Imagine you're climbing a ladder where some rungs are missing. At each step, you can only reach up by a maximum distance of `dist`. If the next rung is too far away, you need to add intermediate rungs to bridge the gap.
|
|
|
|
The key insight is that this problem can be solved **greedily** by processing each gap independently. For any gap between your current position and the next rung, you need to figure out the minimum number of rungs to insert so that no two consecutive rungs (including the ones you add) are more than `dist` apart.
|
|
|
|
Think of it like this: if the gap between two rungs is `g`, and you can step at most `dist` at a time, how many intermediate steps do you need? The answer is `ceil(g / dist) - 1`. We subtract 1 because the final step lands on the existing rung, not a new one.
|
|
|
|
This can be computed as `(g - 1) // dist` using integer arithmetic, which gives us the number of additional rungs needed for that gap.
|
|
|
|
approach: |
|
|
We solve this using a **Single Pass Greedy** approach:
|
|
|
|
**Step 1: Initialise variables**
|
|
|
|
- `prev`: Set to `0` representing our starting position (the floor)
|
|
- `rungs_needed`: Set to `0` to count total rungs we need to add
|
|
|
|
|
|
|
|
**Step 2: Iterate through each rung**
|
|
|
|
- For each rung at height `h`, calculate the gap: `gap = h - prev`
|
|
- If `gap > dist`, we need to add rungs to bridge this gap
|
|
- The number of rungs to add is `(gap - 1) // dist` — this computes how many intermediate steps are needed
|
|
- Add this count to `rungs_needed`
|
|
- Update `prev = h` to move to the current rung
|
|
|
|
|
|
|
|
**Step 3: Return the result**
|
|
|
|
- Return `rungs_needed` after processing all rungs
|
|
|
|
|
|
|
|
This greedy approach works because each gap is independent — the number of rungs needed to bridge one gap doesn't affect any other gap. We simply sum up the rungs needed for each gap.
|
|
|
|
common_pitfalls:
|
|
- title: Off-By-One Error in Rung Calculation
|
|
description: |
|
|
A common mistake is using `gap // dist` instead of `(gap - 1) // dist`.
|
|
|
|
Consider a gap of 6 with `dist = 3`. Using `gap // dist` gives `6 // 3 = 2` rungs. But actually:
|
|
- From position 0, you can reach height 3 (one new rung at 3)
|
|
- From position 3, you can reach height 6 (the existing rung)
|
|
|
|
So you only need **1** rung, not 2. The formula `(gap - 1) // dist` correctly gives `(6 - 1) // 3 = 1`.
|
|
wrong_approach: "Using gap // dist"
|
|
correct_approach: "Using (gap - 1) // dist"
|
|
|
|
- title: Forgetting the Floor
|
|
description: |
|
|
Don't forget that you start at height `0` (the floor), not at the first rung. The gap between the floor and the first rung might also require additional rungs.
|
|
|
|
For example, with `rungs = [5]` and `dist = 2`, you need rungs at heights 2 and 4 to reach height 5. That's 2 additional rungs.
|
|
wrong_approach: "Starting iteration from rungs[1]"
|
|
correct_approach: "Initialize prev = 0 to account for the floor"
|
|
|
|
- title: Integer Overflow Concerns
|
|
description: |
|
|
With `rungs[i]` up to `10^9`, gaps can be very large. In some languages, you might need to be careful about integer overflow when computing gaps. In Python, integers have arbitrary precision, so this isn't a concern.
|
|
|
|
key_takeaways:
|
|
- "**Greedy independence**: When subproblems don't affect each other (each gap is independent), solve them separately and sum the results"
|
|
- "**Integer division trick**: To find how many steps of size `dist` fit in a gap, use `(gap - 1) // dist` to avoid off-by-one errors"
|
|
- "**Don't forget boundaries**: Always consider edge cases like the starting position (floor at height 0)"
|
|
- "**Linear scan sufficiency**: When you only need to make local decisions, a single pass through the data is often enough"
|
|
|
|
time_complexity: "O(n). We iterate through each rung exactly once, where `n` is the length of the `rungs` array."
|
|
space_complexity: "O(1). We only use two variables (`prev` and `rungs_needed`), regardless of input size."
|
|
|
|
solutions:
|
|
- approach_name: Single Pass Greedy
|
|
is_optimal: true
|
|
code: |
|
|
def add_rungs(rungs: list[int], dist: int) -> int:
|
|
# Start at the floor (height 0)
|
|
prev = 0
|
|
rungs_needed = 0
|
|
|
|
for height in rungs:
|
|
# Calculate the gap from current position to this rung
|
|
gap = height - prev
|
|
|
|
# If gap is too large, we need intermediate rungs
|
|
if gap > dist:
|
|
# Number of rungs needed: (gap - 1) // dist
|
|
# We subtract 1 because landing on the target rung doesn't count
|
|
rungs_needed += (gap - 1) // dist
|
|
|
|
# Move to current rung
|
|
prev = height
|
|
|
|
return rungs_needed
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass through the rungs array.
|
|
|
|
**Space Complexity:** O(1) — Only two integer variables used.
|
|
|
|
For each rung, we calculate the gap from our current position and determine how many intermediate rungs are needed using the formula `(gap - 1) // dist`. This accounts for the fact that the final step lands on an existing rung.
|
|
|
|
- approach_name: Simulation
|
|
is_optimal: false
|
|
code: |
|
|
def add_rungs(rungs: list[int], dist: int) -> int:
|
|
prev = 0
|
|
rungs_needed = 0
|
|
|
|
for height in rungs:
|
|
# Simulate climbing step by step
|
|
while height - prev > dist:
|
|
# Add a rung at the maximum reachable height
|
|
prev += dist
|
|
rungs_needed += 1
|
|
|
|
# Now we can reach the current rung
|
|
prev = height
|
|
|
|
return rungs_needed
|
|
explanation: |
|
|
**Time Complexity:** O(n + k) — where k is the total number of rungs added. In the worst case, this could be O(n * max_gap / dist).
|
|
|
|
**Space Complexity:** O(1) — Only two integer variables used.
|
|
|
|
This approach simulates the actual climbing process by adding one rung at a time. While correct and intuitive, it's less efficient than the mathematical formula because it might iterate many times for large gaps. For example, a gap of 10^9 with dist = 1 would require 10^9 iterations for that single gap.
|