1042. Flower Planting With No Adjacent
Problem Description
You are given n
gardens numbered from 1
to n
, and an array paths
where each element paths[i] = [xi, yi]
represents a bidirectional path connecting garden xi
with garden yi
.
Your task is to plant flowers in each garden following these rules:
- You have 4 types of flowers available (numbered 1, 2, 3, and 4)
- Each garden must be planted with exactly one type of flower
- Any two gardens that are directly connected by a path must have different flower types
- Each garden has at most 3 paths connecting to other gardens
You need to return an array answer
of length n
, where answer[i]
represents the flower type planted in garden (i+1)
. The problem guarantees that a valid solution always exists.
For example, if you have 3 gardens with paths [[1,2], [2,3]]
, a valid answer could be [1, 2, 1]
since:
- Garden 1 has flower type 1
- Garden 2 has flower type 2 (different from garden 1, which it's connected to)
- Garden 3 has flower type 1 (different from garden 2, which it's connected to)
The key insight is that since each garden has at most 3 connections and we have 4 flower types available, we can always find a valid flower type for each garden by checking which types are already used by its neighbors.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The problem explicitly involves gardens (nodes) and paths between them (edges). We have
n
gardens andpaths
array that describes bidirectional connections between gardens.
Is it a tree?
- No: A tree has exactly
n-1
edges forn
nodes and no cycles. Our graph can have up to 3 connections per garden and may contain cycles. The problem doesn't specify it's a tree structure.
Is the problem related to directed acyclic graphs?
- No: The paths are bidirectional (undirected graph), not directed. Also, cycles may exist in the graph.
Is the problem related to shortest paths?
- No: We're not finding shortest paths between gardens. We're assigning flower types to ensure adjacent gardens have different flowers.
Does the problem involve connectivity?
- No: We're not checking if gardens are connected or finding connected components. We're coloring the graph nodes.
Does the problem have small constraints?
- Yes: Each garden has at most 3 paths (degree β€ 3), and we only have 4 flower types. This is a small, bounded constraint.
DFS/backtracking?
- Yes: With small constraints and the need to assign values (flower types) while checking neighbors, DFS/backtracking is appropriate for exploring and assigning flower types to each garden.
Conclusion: The flowchart suggests using DFS/backtracking for this graph coloring problem with small constraints. The solution uses a greedy approach that works due to the constraint that each node has at most 3 neighbors and we have 4 colors available.
Intuition
The key insight comes from understanding the constraint that each garden has at most 3 paths connecting to other gardens, while we have 4 flower types available. This is the crucial observation that makes the problem solvable with a simple greedy approach.
Think about it this way: when we're trying to assign a flower type to a garden, in the worst case, it could be connected to 3 other gardens. If all 3 neighboring gardens have different flower types, they can only use up 3 out of our 4 available flower types. This means we'll always have at least 1 flower type remaining that we can use for the current garden.
This is why we don't need complex backtracking or checking for conflicts later - we can simply process each garden one by one and greedily assign the first available flower type that hasn't been used by its neighbors.
The approach mirrors the classic graph coloring problem, but with a guarantee that we'll never get stuck. For each garden:
- Look at all the gardens connected to it
- See what flower types those connected gardens are using
- Pick the first flower type from
{1, 2, 3, 4}
that isn't being used by any neighbor
Since we process gardens sequentially and each garden can "block" at most 3 flower types (from its at most 3 neighbors), we're guaranteed to always find a valid flower type from our set of 4.
This is why the solution doesn't need to backtrack or reconsider previous choices - the mathematical relationship between maximum degree (3) and number of colors (4) ensures that a greedy assignment will always work.
Learn more about Depth-First Search, Breadth-First Search and Graph patterns.
Solution Approach
The implementation follows a straightforward greedy coloring strategy:
Step 1: Build the Graph
First, we construct an adjacency list representation of the garden connections. We use a defaultdict(list)
to store the graph where g[x]
contains all gardens adjacent to garden x
. Since the problem uses 1-based indexing for gardens but Python uses 0-based indexing, we convert by subtracting 1 from each garden number:
g = defaultdict(list)
for x, y in paths:
x, y = x - 1, y - 1 # Convert to 0-based indexing
g[x].append(y)
g[y].append(x) # Add both directions since paths are bidirectional
Step 2: Initialize the Result Array
We create an array ans
of size n
initialized with zeros to store the flower type for each garden:
ans = [0] * n
Step 3: Assign Flower Types to Each Garden
For each garden x
from 0
to n-1
, we:
-
Find used flower types: Create a set containing the flower types already assigned to all neighboring gardens:
used = {ans[y] for y in g[x]}
This set comprehension looks at each neighbor
y
of gardenx
and collects their assigned flower types. If a neighbor hasn't been assigned yet (ans[y] = 0
), it gets added to the set but doesn't affect our choice since we only check for flower types 1-4. -
Choose the first available flower type: Iterate through flower types 1 to 4 and pick the first one not in the
used
set:for c in range(1, 5): if c not in used: ans[x] = c break
Step 4: Return the Result
After processing all gardens, return the ans
array containing the flower type assignments.
The time complexity is O(n + m)
where n
is the number of gardens and m
is the number of paths, as we process each garden once and examine each edge twice (once from each endpoint). The space complexity is O(n + m)
for storing the graph and the result array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with 4 gardens and paths [[1,2], [3,4], [4,1], [2,3]]
.
Initial Setup:
- Gardens: 1, 2, 3, 4
- Paths create connections: 1β2, 3β4, 4β1, 2β3
- Result array:
[0, 0, 0, 0]
(initially no flowers assigned)
Step 1: Build the adjacency list (using 0-based indexing):
- Garden 0 (originally 1): connected to [1, 3]
- Garden 1 (originally 2): connected to [0, 2]
- Garden 2 (originally 3): connected to [3, 1]
- Garden 3 (originally 4): connected to [2, 0]
Step 2: Process each garden greedily
Garden 0:
- Check neighbors: gardens 1 and 3
- Used flower types: {0, 0} = {0} (both neighbors unassigned)
- Available types: 1, 2, 3, 4 all available
- Assign: flower type 1
- Result:
[1, 0, 0, 0]
Garden 1:
- Check neighbors: gardens 0 and 2
- Used flower types: {1, 0} = {1, 0} (garden 0 has type 1)
- First available type not in {1, 0}: type 2
- Assign: flower type 2
- Result:
[1, 2, 0, 0]
Garden 2:
- Check neighbors: gardens 3 and 1
- Used flower types: {0, 2} = {0, 2} (garden 1 has type 2)
- First available type not in {0, 2}: type 1
- Assign: flower type 1
- Result:
[1, 2, 1, 0]
Garden 3:
- Check neighbors: gardens 2 and 0
- Used flower types: {1, 1} = {1} (both have type 1)
- First available type not in {1}: type 2
- Assign: flower type 2
- Result:
[1, 2, 1, 2]
Verification:
- Garden 1 (type 1) β Garden 2 (type 2) β Different
- Garden 3 (type 1) β Garden 4 (type 2) β Different
- Garden 4 (type 2) β Garden 1 (type 1) β Different
- Garden 2 (type 2) β Garden 3 (type 1) β Different
The greedy approach successfully assigns flower types without conflicts. Note how we never needed to backtrack - at each step, we always had at least one flower type available because each garden has at most 3 neighbors but we have 4 flower types to choose from.
Solution Implementation
1from collections import defaultdict
2from typing import List
3
4class Solution:
5 def gardenNoAdj(self, n: int, paths: List[List[int]]) -> List[int]:
6 # Build adjacency list for the graph
7 # Key: garden index (0-based), Value: list of adjacent garden indices
8 adjacency_graph = defaultdict(list)
9
10 # Convert paths to 0-based indexing and build bidirectional edges
11 for garden1, garden2 in paths:
12 # Convert from 1-based to 0-based indexing
13 garden1_idx = garden1 - 1
14 garden2_idx = garden2 - 1
15
16 # Add bidirectional edges
17 adjacency_graph[garden1_idx].append(garden2_idx)
18 adjacency_graph[garden2_idx].append(garden1_idx)
19
20 # Initialize result array to store flower type for each garden
21 # 0 means no flower type assigned yet
22 flower_assignments = [0] * n
23
24 # Process each garden one by one
25 for current_garden in range(n):
26 # Collect flower types already used by neighboring gardens
27 used_flower_types = {flower_assignments[neighbor]
28 for neighbor in adjacency_graph[current_garden]}
29
30 # Try assigning flower types 1 through 4
31 for flower_type in range(1, 5):
32 # If this flower type is not used by any neighbor
33 if flower_type not in used_flower_types:
34 # Assign this flower type to current garden
35 flower_assignments[current_garden] = flower_type
36 break # Move to next garden
37
38 return flower_assignments
39
1class Solution {
2 public int[] gardenNoAdj(int n, int[][] paths) {
3 // Create adjacency list to represent the graph
4 // Each garden (node) will store its connected gardens
5 List<Integer>[] adjacencyList = new List[n];
6 Arrays.setAll(adjacencyList, index -> new ArrayList<>());
7
8 // Build the undirected graph from the paths
9 // Convert 1-indexed gardens to 0-indexed for array access
10 for (int[] path : paths) {
11 int garden1 = path[0] - 1;
12 int garden2 = path[1] - 1;
13 adjacencyList[garden1].add(garden2);
14 adjacencyList[garden2].add(garden1);
15 }
16
17 // Array to store the flower type (1-4) for each garden
18 int[] flowerAssignment = new int[n];
19
20 // Boolean array to track which flower types are used by neighbors
21 // Index 0 is unused, indices 1-4 represent flower types
22 boolean[] usedFlowers = new boolean[5];
23
24 // Process each garden to assign a flower type
25 for (int currentGarden = 0; currentGarden < n; currentGarden++) {
26 // Reset the used flowers array for current garden
27 Arrays.fill(usedFlowers, false);
28
29 // Mark flower types that are already used by neighboring gardens
30 for (int neighbor : adjacencyList[currentGarden]) {
31 usedFlowers[flowerAssignment[neighbor]] = true;
32 }
33
34 // Find the first available flower type (1-4) and assign it
35 for (int flowerType = 1; flowerType <= 4; flowerType++) {
36 if (!usedFlowers[flowerType]) {
37 flowerAssignment[currentGarden] = flowerType;
38 break;
39 }
40 }
41 }
42
43 return flowerAssignment;
44 }
45}
46
1class Solution {
2public:
3 vector<int> gardenNoAdj(int n, vector<vector<int>>& paths) {
4 // Build adjacency list for the garden graph
5 // Each garden can have at most 3 neighbors
6 vector<vector<int>> adjacencyList(n);
7
8 // Convert 1-indexed paths to 0-indexed and build bidirectional edges
9 for (const auto& path : paths) {
10 int garden1 = path[0] - 1; // Convert to 0-indexed
11 int garden2 = path[1] - 1; // Convert to 0-indexed
12
13 // Add bidirectional edges
14 adjacencyList[garden1].push_back(garden2);
15 adjacencyList[garden2].push_back(garden1);
16 }
17
18 // Result array to store flower type (1-4) for each garden
19 vector<int> flowerAssignment(n, 0);
20
21 // Array to track which flower types are used by neighbors
22 bool usedFlowers[5]; // Index 0 unused, indices 1-4 for flower types
23
24 // Assign flower type to each garden
25 for (int currentGarden = 0; currentGarden < n; ++currentGarden) {
26 // Reset the used flowers array for current garden
27 memset(usedFlowers, false, sizeof(usedFlowers));
28
29 // Mark flower types already used by neighboring gardens
30 for (int neighbor : adjacencyList[currentGarden]) {
31 usedFlowers[flowerAssignment[neighbor]] = true;
32 }
33
34 // Find the first available flower type (1-4) and assign it
35 for (int flowerType = 1; flowerType <= 4; ++flowerType) {
36 if (!usedFlowers[flowerType]) {
37 flowerAssignment[currentGarden] = flowerType;
38 break;
39 }
40 }
41 }
42
43 return flowerAssignment;
44 }
45};
46
1/**
2 * Assigns flower types to gardens such that no two adjacent gardens have the same flower type.
3 * Each garden can have one of 4 flower types (represented as 1, 2, 3, or 4).
4 *
5 * @param n - Number of gardens (labeled from 1 to n)
6 * @param paths - Array of paths where each path [x, y] connects garden x to garden y
7 * @returns Array where ans[i] represents the flower type assigned to garden i+1
8 */
9function gardenNoAdj(n: number, paths: number[][]): number[] {
10 // Build adjacency list representation of the graph
11 // Each index represents a garden, containing an array of its neighboring gardens
12 const adjacencyList: number[][] = Array.from({ length: n }, () => []);
13
14 // Populate the adjacency list (converting 1-indexed to 0-indexed)
15 for (const [garden1, garden2] of paths) {
16 adjacencyList[garden1 - 1].push(garden2 - 1);
17 adjacencyList[garden2 - 1].push(garden1 - 1);
18 }
19
20 // Initialize result array to store flower type for each garden
21 const flowerAssignments: number[] = Array(n).fill(0);
22
23 // Process each garden sequentially
24 for (let currentGarden = 0; currentGarden < n; ++currentGarden) {
25 // Track which flower types are already used by neighboring gardens
26 // Index 0 is unused, indices 1-4 represent flower types 1-4
27 const usedFlowerTypes: boolean[] = Array(5).fill(false);
28
29 // Mark flower types used by all neighboring gardens
30 for (const neighborGarden of adjacencyList[currentGarden]) {
31 usedFlowerTypes[flowerAssignments[neighborGarden]] = true;
32 }
33
34 // Assign the first available flower type (1-4) to current garden
35 for (let flowerType = 1; flowerType < 5; ++flowerType) {
36 if (!usedFlowerTypes[flowerType]) {
37 flowerAssignments[currentGarden] = flowerType;
38 break;
39 }
40 }
41 }
42
43 return flowerAssignments;
44}
45
Time and Space Complexity
Time Complexity: O(n + m)
The algorithm consists of two main parts:
- Building the adjacency list: We iterate through all
m
paths once, and for each path, we perform constant-time operations (appending to lists). This takesO(m)
time. - Assigning colors to gardens: We iterate through all
n
gardens. For each gardenx
, we:- Create a set of used colors by checking all neighbors of
x
. Since each garden can have at most 3 neighbors (as we have 4 colors available and the graph is guaranteed to be colorable), this takesO(1)
time. - Find an unused color from the 4 available colors, which takes
O(1)
time in the worst case.
- Create a set of used colors by checking all neighbors of
Therefore, the total time complexity is O(m) + O(n) = O(n + m)
.
Space Complexity: O(n + m)
The space usage includes:
- The adjacency list
g
: In the worst case, it stores all edges twice (once for each direction), requiringO(m)
space. - The answer array
ans
: Stores one color for each of then
gardens, requiringO(n)
space. - The
used
set: Contains at most 3 elements at any time (bounded by the number of neighbors), which isO(1)
space.
Therefore, the total space complexity is O(n + m)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Index Confusion Between 1-Based and 0-Based
The most common mistake is forgetting to convert between 1-based garden numbering (used in the problem) and 0-based array indexing (used in Python).
Incorrect approach:
# Forgetting to convert indices for x, y in paths: g[x].append(y) # Wrong! x and y are 1-based g[y].append(x)
Why it fails: This would try to access g[n]
when the maximum garden number is n
, causing an IndexError or incorrect adjacency relationships.
Solution: Always convert to 0-based indexing immediately:
for x, y in paths: x, y = x - 1, y - 1 # Convert to 0-based first g[x].append(y) g[y].append(x)
2. Forgetting Bidirectional Paths
Another common mistake is treating paths as unidirectional, only adding one direction to the adjacency list.
Incorrect approach:
for x, y in paths: x, y = x - 1, y - 1 g[x].append(y) # Only adding one direction
Why it fails: Garden y
wouldn't know about its connection to garden x
, leading to potential conflicts where both gardens might get assigned the same flower type.
Solution: Always add both directions:
g[x].append(y) g[y].append(x) # Don't forget the reverse edge
3. Not Handling Isolated Gardens
If a garden has no paths connecting to it, it might not appear in the adjacency list at all when using a regular dictionary.
Incorrect approach:
g = {} # Regular dictionary
for current_garden in range(n):
used = {ans[y] for y in g[current_garden]} # KeyError if garden has no paths
Why it fails: Accessing g[current_garden]
for an isolated garden would raise a KeyError.
Solution: Use defaultdict(list)
which returns an empty list for missing keys:
g = defaultdict(list)
# Now g[isolated_garden] returns [] instead of raising KeyError
4. Assuming Gardens Must Be Processed in Order
While the greedy approach works when processing gardens sequentially, some might mistakenly think they need to process gardens in a specific order (like BFS/DFS traversal).
Why it's unnecessary: Since each garden has at most 3 connections and we have 4 flower types, we're guaranteed to always have at least one available flower type regardless of processing order. The sequential approach (0 to n-1) is sufficient and simpler.
Which data structure is used in a depth first search?
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
Want a Structured Path to Master System Design Too? Donβt Miss This!