Problem
You are given an integer limit
and a 2D array queries
of size n x 2
.
There are limit + 1
balls with distinct labels in the range [0, limit]
. Initially, all balls are uncolored. For every query in queries
that is of the form [x, y]
, you mark ball x
with the color y
. After each query, you need to find the number of distinct colors among the balls.
Return an array result
of length n
, where result[i]
denotes the number of distinct colors after ith
query.
Note that when answering a query, lack of a color will not be considered as a color.
Examples
Example 1:
Input: limit = 4, queries = [[1,4],[2,5],[1,3],[3,4]]
Output: [1,2,2,3]
Explanation:
* After query 0, ball 1 has color 4.
* After query 1, ball 1 has color 4, and ball 2 has color 5.
* After query 2, ball 1 has color 3, and ball 2 has color 5.
* After query 3, ball 1 has color 3, ball 2 has color 5, and ball 3 has color 4.
Example 2:
Input: limit = 4, queries = [[0,1],[1,2],[2,2],[3,4],[4,5]]
Output: [1,2,2,3,4]
Explanation:
* After query 0, ball 0 has color 1.
* After query 1, ball 0 has color 1, and ball 1 has color 2.
* After query 2, ball 0 has color 1, and balls 1 and 2 have color 2.
* After query 3, ball 0 has color 1, balls 1 and 2 have color 2, and ball 3 has color 4.
* After query 4, ball 0 has color 1, balls 1 and 2 have color 2, ball 3 has color 4, and ball 4 has color 5.
Constraints:
1 <= limit <= 10^9
1 <= n == queries.length <= 10^5
queries[i].length == 2
0 <= queries[i][0] <= limit
1 <= queries[i][1] <= 10^9
Solution
The key challenge is to efficiently track and manage the colors of the balls after each query. Each query specifies a ball and a color, and our goal is to determine the number of distinct colors present after processing each query.
Video explanation
Here is the video explaining below methods in detail. Please check it out:
Method 1 - Naive
The naive approach is straightforward but inefficient. It involves updating the color of the specified ball for each query, followed by a linear scan of the entire list of balls to count the number of distinct colors.
Complexity
- ⏰ Time complexity:
O(n * m)
, wheren
is the number of queries andm
is the number of balls. This is because, for each query, performing a linear scan to count the distinct colors takesO(m)
time. - 🧺 Space complexity:
O(m)
for storing the balls in balls array.
Method 2 - Using array for balls and map for colors
We have to track the:
- color of each ball to update and check existing colors, and
- number of occurrences for each color to efficiently calculate distinct colors.
For that we can use:
- An array (
balls
) where the index represents the ball and the value at that index represents the current color of the ball. - A hashmap (
colorMap
) to maintain the count of each color.
Here is the approach:
- Initialization:
n
: number of queries.ans
: array for the number of distinct colors after each query.balls
: array to track the color of each ball.colorMap
: hashmap for color counts.
- Iterate Through Queries: For each query:
- Extract ball and color.
- If the ball already has a color, adjust
colorMap
. - Update the ball’s color in
balls
. - Update the new color count in
colorMap
. - Store the count of distinct colors in
ans
.
- Final Result: Return the
ans
array.
Code
Java
class Solution {
public int[] queryResults(int limit, int[][] queries) {
int n = queries.length;
int[] ans = new int[n];
Map<Integer, Integer> colorMap = new HashMap<>();
int[] balls = new int[limit + 1];
for (int i = 0; i < n; i++) {
int ball = queries[i][0];
int color = queries[i][1];
if (balls[ball] != 0) {
int prevColor = balls[ball];
colorMap.put(prevColor, colorMap.get(prevColor) - 1);
if (colorMap.get(prevColor) == 0) {
colorMap.remove(prevColor);
}
}
balls[ball] = color;
colorMap.put(color, colorMap.getOrDefault(color, 0) + 1);
ans[i] = colorMap.size();
}
return ans;
}
}
Python
class Solution:
def queryResults(self, limit: int, queries: List[List[int]]) -> List[int]:
n = len(queries)
ans = [0] * n
color_map: Dict[int, int] = {}
balls = [0] * (limit + 1)
for i, (ball, color) in enumerate(queries):
if balls[ball] != 0:
prev_color = balls[ball]
color_map[prev_color] = color_map.get(prev_color, 0) - 1
if color_map[prev_color] == 0:
del color_map[prev_color]
balls[ball] = color
color_map[color] = color_map.get(color, 0) + 1
ans[i] = len(color_map)
return ans
Complexity
- ⏰ Time complexity:
O(n)
wheren
is the number of queries, as each query involves constant-time operations for updating the arrays and hashmap. - 🧺 Space complexity:
O(n + m)
wherem
is the limit (number of distinct ball labels), because we need to store color information for up tom + 1
balls and at mostn
distinct colors in colormap.
Method 3 - Using 2 maps
The main challenge in the previous solution is the large memory usage due to the array of size limit + 1
. Given that limit
can be as large as 10^9
, using such a huge array is excessive, especially since not all ball labels are accessed in the queries (with n
ranging up to 10^5
).
To improve storage efficiency, we can eliminate wasted space by using a hashmap to store only the necessary ball labels accessed by the queries. This will significantly optimize space complexity and prevent memory overuse.
By using hashmaps for both the ball labels and colors, we maintain the same logic as before but with much more efficient memory usage. This allows us to process and track query results while staying within the memory limits.a
Code
Java
class Solution {
public int[] queryResults(int limit, int[][] queries) {
int n = queries.length;
int[] ans = new int[n];
Map<Integer, Integer> colorMap = new HashMap<>();
Map<Integer, Integer> ballMap = new HashMap<>();
for (int i = 0; i < n; i++) {
int ball = queries[i][0];
int color = queries[i][1];
if (ballMap.containsKey(ball)) {
int prevColor = ballMap.get(ball);
colorMap.put(prevColor, colorMap.get(prevColor) - 1);
if (colorMap.get(prevColor) == 0) {
colorMap.remove(prevColor);
}
}
ballMap.put(ball, color);
colorMap.put(color, colorMap.getOrDefault(color, 0) + 1);
ans[i] = colorMap.size();
}
return ans;
}
}
Python
class Solution:
def queryResults(self, limit: int, queries: List[List[int]]) -> List[int]:
n = len(queries)
ans = [0] * n
color_map: Dict[int, int] = {}
ball_map: Dict[int, int] = {}
for i, (ball, color) in enumerate(queries):
if ball in ball_map:
prev_color = ball_map[ball]
color_map[prev_color] -= 1
if color_map[prev_color] == 0:
del color_map[prev_color]
ball_map[ball] = color
color_map[color] = color_map.get(color, 0) + 1
ans[i] = len(color_map)
return ans
Complexity
- ⏰ Time complexity:
O(n)
. The algorithm processes each query once, performingO(1)
operations on average for each due to hashmaps. Thus, the overall time complexity isO(n)
. - 🧺 Space complexity:
O(n)
.ballMap
andcolorMap
together can store up ton
entries in total, resulting in a space complexity ofO(n)
. The result array also contributesO(n)
space, but it is typically part of the output.