3160. Find the Number of Distinct Colors Among the Balls
Problem Description
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 the i
th query.
Note: When answering a query, lack of a color will not be considered as a color.
Intuition
To solve this problem, we need to efficiently keep track of the colors assigned to each ball and determine the number of distinct colors after each query.
The solution involves using two hash tables:
-
Color Assignment Table (
g
): This tracks the current color assigned to each ball. For every query[x, y]
, we update the color of ballx
toy
. -
Color Count Table (
cnt
): This keeps a count of how many balls currently use each color. Every time we change a ball's color, we update the count of the old and new colors. If a color's count becomes zero, we remove it, thereby ensuring we only track colors currently used.
After applying a query, the number of distinct colors is simply the size of the color count table (cnt
), which is appended to the result list.
By maintaining these two tables, we efficiently manage color assignments and quickly compute the number of distinct colors as required.
Solution Approach
The solution utilizes a double hash table approach to keep track of the ball color assignments and the number of distinct colors.
-
Color Assignment Table (
g
): This dictionary records the current color of each ball. When we receive a query[x, y]
, it tells us what color to assign to the ballx
. -
Color Count Table (
cnt
): This utilizes aCounter
, from thecollections
module in Python, to keep track of how many balls have a specific color. This helps in quickly assessing the number of distinct colors at any point.
Steps:
- Initialize an empty dictionary
g
to store current color assignments for each ball. - Utilize a
Counter
calledcnt
to count occurrences of each color. - Initialize an empty list
ans
to store the results after each query.
For each query [x, y]
:
- Increment the count of color
y
incnt
by one. - If ball
x
was previously colored (exists ing
), reduce the count of its old color incnt
. If the decremented count drops to zero, remove that color fromcnt
. - Update the color of ball
x
toy
in theg
dictionary. - Append the current number of distinct colors, which is the size of
cnt
, to the result listans
.
Finally, return the list ans
, containing the number of distinct colors after each query.
The solution effectively combines efficient data structures to update and track color data dynamically, enabling quick access to the required information after each query.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's go through an example to demonstrate the solution approach.
Input:
limit = 4
queries = [[0, 1], [2, 2], [0, 2], [3, 1]]
The goal is to determine the number of distinct colors on the balls after each query.
Initial Setup:
- Color Assignment Table (
g
): An empty dictionary to track the color of each ball. - Color Count Table (
cnt
): ACounter
to keep count of how many balls are using each color. - Result List (
ans
): An empty list to store the number of distinct colors after each query.
Process Queries:
-
Query [0, 1]:
- Color ball
0
with color1
. - Update
cnt
: Increment color1
by 1. Now,cnt
is{1: 1}
. - Record that ball
0
has color1
ing
:g = {0: 1}
. - There is 1 distinct color, so append
1
toans
:ans = [1]
.
- Color ball
-
Query [2, 2]:
- Color ball
2
with color2
. - Update
cnt
: Increment color2
by 1. Now,cnt
is{1: 1, 2: 1}
. - Record that ball
2
has color2
ing
:g = {0: 1, 2: 2}
. - There are 2 distinct colors, so append
2
toans
:ans = [1, 2]
.
- Color ball
-
Query [0, 2]:
- Change ball
0
's color from1
to2
. - Update
cnt
:- Reduce count of color
1
by 1. Since count becomes zero, remove it:cnt = {2: 1}
. - Increment count of color
2
by 1.cnt
becomes{2: 2}
.
- Reduce count of color
- Update
g
to reflect ball0
's new color:g = {0: 2, 2: 2}
. - Still only 1 distinct color, so append
1
toans
:ans = [1, 2, 1]
.
- Change ball
-
Query [3, 1]:
- Color ball
3
with color1
. - Update
cnt
: Increment color1
by 1. Now,cnt
is{2: 2, 1: 1}
. - Record that ball
3
has color1
ing
:g = {0: 2, 2: 2, 3: 1}
. - There are 2 distinct colors, so append
2
toans
:ans = [1, 2, 1, 2]
.
- Color ball
Final Result:
- The
ans
array, representing the number of distinct colors after each query, is[1, 2, 1, 2]
.
This walkthrough encapsulates the step-by-step process, utilizing hash tables to efficiently maintain and count colors on the balls.
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def queryResults(self, limit: int, queries: List[List[int]]) -> List[int]:
6 # Dictionary to store the mapping of 'x' -> 'y' values
7 mapping = {}
8
9 # Counter to keep track of the frequency of each 'y' value
10 frequency_counter = Counter()
11
12 # List to store results
13 results = []
14
15 # Process each query which is a pair (x, y)
16 for x, y in queries:
17 # Increase the counter for 'y'
18 frequency_counter[y] += 1
19
20 # If 'x' already exists in the mapping, update the counter for the old 'y' value
21 if x in mapping:
22 old_y = mapping[x]
23 frequency_counter[old_y] -= 1
24
25 # Remove entry from counter if the frequency becomes zero
26 if frequency_counter[old_y] == 0:
27 frequency_counter.pop(old_y)
28
29 # Update the mapping with the new 'y' value for 'x'
30 mapping[x] = y
31
32 # Append the number of unique 'y' values to the results
33 results.append(len(frequency_counter))
34
35 return results
36
1import java.util.HashMap;
2import java.util.Map;
3
4class Solution {
5 public int[] queryResults(int limit, int[][] queries) {
6 // Map to store the current group for each element
7 Map<Integer, Integer> groupMap = new HashMap<>();
8 // Map to count the occurrences of each group
9 Map<Integer, Integer> countMap = new HashMap<>();
10
11 int queryLength = queries.length;
12 int[] results = new int[queryLength];
13
14 for (int i = 0; i < queryLength; ++i) {
15 int element = queries[i][0];
16 int newGroup = queries[i][1];
17
18 // Increase the count for the new group
19 countMap.merge(newGroup, 1, Integer::sum);
20
21 // If the element already has a group, decrease the count of the old group
22 if (groupMap.containsKey(element)) {
23 int oldGroup = groupMap.get(element);
24 if (countMap.merge(oldGroup, -1, Integer::sum) == 0) {
25 countMap.remove(oldGroup);
26 }
27 }
28
29 // Update the group of the element
30 groupMap.put(element, newGroup);
31
32 // Store the count of different groups in the result array
33 results[i] = countMap.size();
34 }
35
36 return results;
37 }
38}
39
1#include <vector>
2#include <unordered_map>
3
4using namespace std;
5
6class Solution {
7public:
8 vector<int> queryResults(int limit, vector<vector<int>>& queries) {
9 // g is a hash map that maps each x to its current y
10 unordered_map<int, int> mapping;
11 // cnt is a hash map that counts the occurrences of each y
12 unordered_map<int, int> count;
13 // ans will store the results of the query
14 vector<int> result;
15
16 for (auto& query : queries) {
17 int x = query[0];
18 int y = query[1];
19
20 // Increment the count of y in count map
21 count[y]++;
22
23 // Check if x already exists in mapping and adjust the count
24 if (mapping.contains(x) && --count[mapping[x]] == 0) {
25 // If count of previous y becomes zero, remove it
26 count.erase(mapping[x]);
27 }
28
29 // Update mapping for x to the new y
30 mapping[x] = y;
31
32 // Add the current distinct count of y's to the result
33 result.push_back(count.size());
34 }
35
36 return result;
37 }
38};
39
1// The function queryResults processes a series of queries and returns an array indicating
2// the number of unique values in the map `cnt` after each query.
3
4function queryResults(limit: number, queries: number[][]): number[] {
5 // `g` maps keys to their currently assigned value.
6 const g = new Map<number, number>();
7
8 // `cnt` tracks the frequency of each value within `g`.
9 const cnt = new Map<number, number>();
10
11 // Array `ans` will store the number of unique values in `cnt` after each query.
12 const ans: number[] = [];
13
14 // Process each query in the input array `queries`.
15 for (const [x, y] of queries) {
16 // Increment the frequency of `y` in `cnt`.
17 cnt.set(y, (cnt.get(y) ?? 0) + 1);
18
19 if (g.has(x)) {
20 // If `x` has a previously assigned value, decrease the frequency of that value in `cnt`.
21 const previousValue = g.get(x)!;
22 cnt.set(previousValue, cnt.get(previousValue)! - 1);
23
24 // If the frequency of the previous value becomes zero, remove it from `cnt`.
25 if (cnt.get(previousValue) === 0) {
26 cnt.delete(previousValue);
27 }
28 }
29
30 // Assign the new value `y` to `x` in map `g`.
31 g.set(x, y);
32
33 // Append the current number of unique values in `cnt` to `ans`.
34 ans.push(cnt.size);
35 }
36
37 // Return the array of unique value counts.
38 return ans;
39}
40
Time and Space Complexity
The time complexity of the code is O(m)
, where m
is the length of the array queries
. This is because the code iterates through each query exactly once, and each operation inside the loop (such as updating the cnt
counter or the dictionary g
) is done in constant time.
The space complexity is also O(m)
, as the space used by the dictionary g
and cnt
counter is proportional to the number of queries, with each new entry being based on the elements within queries
.
Learn more about how to find time and space complexity quickly.
Depth first search is equivalent to which of the tree traversal order?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!