Files
codetutor/backend/data/questions/can-place-flowers.yaml
2025-05-25 10:16:13 +01:00

176 lines
8.0 KiB
YAML

title: Can Place Flowers
slug: can-place-flowers
difficulty: easy
leetcode_id: 605
leetcode_url: https://leetcode.com/problems/can-place-flowers/
categories:
- arrays
patterns:
- greedy
description: |
You have a long flowerbed in which some of the plots are planted, and some are not. However, flowers cannot be planted in **adjacent** plots.
Given an integer array `flowerbed` containing `0`'s and `1`'s, where `0` means empty and `1` means not empty, and an integer `n`, return `true` *if* `n` *new flowers can be planted in the flowerbed without violating the no-adjacent-flowers rule and* `false` *otherwise*.
constraints: |
- `1 <= flowerbed.length <= 2 * 10^4`
- `flowerbed[i]` is `0` or `1`
- There are no two adjacent flowers in `flowerbed`
- `0 <= n <= flowerbed.length`
examples:
- input: "flowerbed = [1,0,0,0,1], n = 1"
output: "true"
explanation: "We can plant one flower at index 2. The flowerbed becomes [1,0,1,0,1], which satisfies the no-adjacent-flowers rule."
- input: "flowerbed = [1,0,0,0,1], n = 2"
output: "false"
explanation: "There is only one valid spot (index 2) to plant a flower. We cannot plant 2 flowers without violating the adjacency rule."
explanation:
intuition: |
Imagine walking through a garden path where each plot is either empty (`0`) or has a flower (`1`). Your task is to plant as many flowers as possible without placing two flowers next to each other.
The key insight is that we can make a **greedy decision** at each empty plot: if we *can* plant a flower here (no adjacent flowers), we *should* plant it immediately. There's never a benefit to "saving" a spot for later — planting now can only help us, never hurt us.
Think of it like this: you're walking left to right. At each empty plot, you check three things:
- Is the plot to my left empty (or am I at the start)?
- Is the current plot empty?
- Is the plot to my right empty (or am I at the end)?
If all three conditions are true, plant a flower and move on. By greedily planting whenever possible, you guarantee the maximum number of flowers.
approach: |
We solve this using a **Single Pass Greedy Approach**:
**Step 1: Initialise a counter**
- `count`: Set to `0` to track how many flowers we've planted
&nbsp;
**Step 2: Iterate through the flowerbed**
- For each position `i`, check if we can plant a flower:
- Current plot must be empty: `flowerbed[i] == 0`
- Left neighbour must be empty or out of bounds: `i == 0` or `flowerbed[i-1] == 0`
- Right neighbour must be empty or out of bounds: `i == len(flowerbed) - 1` or `flowerbed[i+1] == 0`
- If all conditions are met, plant the flower:
- Set `flowerbed[i] = 1` (this prevents planting at `i+1`)
- Increment `count`
&nbsp;
**Step 3: Early termination (optimisation)**
- If `count >= n` at any point, we can return `True` immediately
- No need to continue checking once we've planted enough flowers
&nbsp;
**Step 4: Return the result**
- After the loop, return `count >= n`
&nbsp;
This greedy approach works because planting a flower at the earliest valid position never prevents us from achieving the maximum count — it only restricts the immediately adjacent plot, which wouldn't have been valid anyway.
common_pitfalls:
- title: Forgetting Boundary Conditions
description: |
The first and last plots only have one neighbour to check, not two.
For position `0`, there is no left neighbour — treat it as empty. Similarly, for the last position, there is no right neighbour.
Failing to handle boundaries leads to index-out-of-bounds errors or incorrect results for flowerbeds like `[0,0,1]` where the first plot is plantable.
wrong_approach: "Always checking both neighbours without boundary checks"
correct_approach: "Use `i == 0` or `i == len-1` to handle edge positions"
- title: Not Marking Planted Flowers
description: |
After deciding to plant at position `i`, you must update `flowerbed[i] = 1`.
If you only increment a counter without modifying the array, the next iteration will incorrectly think position `i+1` has an empty left neighbour. This leads to planting adjacent flowers.
Example: In `[0,0,0]`, if you plant at index `0` but don't mark it, you'll also "plant" at index `1`, violating the adjacency rule.
wrong_approach: "Only counting without modifying the array"
correct_approach: "Set flowerbed[i] = 1 after planting"
- title: Checking n > 0 Instead of count >= n
description: |
When `n = 0`, the answer should always be `True` — you don't need to plant any flowers.
Some implementations decrement `n` each time they plant, then check `n <= 0`. This works, but be careful with the initial check: if `n == 0` from the start, return `True` immediately or ensure your loop handles it correctly.
key_takeaways:
- "**Greedy validity**: When a locally optimal choice (plant now) never hurts the global solution, greedy works"
- "**In-place modification**: Updating the input array lets us track state without extra space"
- "**Boundary handling**: Always consider edge cases at array boundaries — they often have special rules"
- "**Early termination**: Once you've achieved the goal (`count >= n`), stop early for efficiency"
time_complexity: "O(n). We traverse the flowerbed array once, where `n` is the length of the array."
space_complexity: "O(1). We modify the input array in-place and use only a constant amount of extra space for the counter."
solutions:
- approach_name: Single Pass Greedy
is_optimal: true
code: |
def can_place_flowers(flowerbed: list[int], n: int) -> bool:
count = 0
length = len(flowerbed)
for i in range(length):
# Check if current plot is empty
if flowerbed[i] == 0:
# Check left neighbour (or start of array)
left_empty = (i == 0) or (flowerbed[i - 1] == 0)
# Check right neighbour (or end of array)
right_empty = (i == length - 1) or (flowerbed[i + 1] == 0)
# If both neighbours are empty, plant here
if left_empty and right_empty:
flowerbed[i] = 1 # Mark as planted
count += 1
# Early exit if we've planted enough
if count >= n:
return True
return count >= n
explanation: |
**Time Complexity:** O(n) — Single pass through the flowerbed.
**Space Complexity:** O(1) — We modify the input array in-place and use only a counter variable.
We iterate through each plot once. When we find a valid spot (current empty, left empty or boundary, right empty or boundary), we plant and mark it. The modification prevents consecutive plantings. Early termination provides a small optimisation when `n` is small.
- approach_name: Count Consecutive Zeros
is_optimal: false
code: |
def can_place_flowers(flowerbed: list[int], n: int) -> bool:
# Pad with zeros for easier boundary handling
padded = [0] + flowerbed + [0]
count = 0
zeros = 0
for plot in padded:
if plot == 0:
zeros += 1
else:
# Calculate flowers that fit in this gap
# For k consecutive zeros, we can fit (k-1)//2 flowers
count += (zeros - 1) // 2
zeros = 0
# Don't forget the trailing zeros
count += (zeros - 1) // 2
return count >= n
explanation: |
**Time Complexity:** O(n) — Single pass through the padded array.
**Space Complexity:** O(n) — We create a new padded array.
This approach counts consecutive zeros between flowers. For `k` consecutive empty plots (including virtual padding), we can plant `(k-1) // 2` flowers. The padding simplifies boundary handling. While correct, this uses extra space compared to the in-place greedy approach.