Facebook Pixel

3160. Find the Number of Distinct Colors Among the Balls

MediumArrayHash TableSimulation
Leetcode Link

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 ith 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:

  1. Color Assignment Table (g): This tracks the current color assigned to each ball. For every query [x, y], we update the color of ball x to y.

  2. 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.

  1. 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 ball x.

  2. Color Count Table (cnt): This utilizes a Counter, from the collections 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 called cnt to count occurrences of each color.
  • Initialize an empty list ans to store the results after each query.

For each query [x, y]:

  1. Increment the count of color y in cnt by one.
  2. If ball x was previously colored (exists in g), reduce the count of its old color in cnt. If the decremented count drops to zero, remove that color from cnt.
  3. Update the color of ball x to y in the g dictionary.
  4. Append the current number of distinct colors, which is the size of cnt, to the result list ans.

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 Evaluator

Example 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): A Counter 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:

  1. Query [0, 1]:

    • Color ball 0 with color 1.
    • Update cnt: Increment color 1 by 1. Now, cnt is {1: 1}.
    • Record that ball 0 has color 1 in g: g = {0: 1}.
    • There is 1 distinct color, so append 1 to ans: ans = [1].
  2. Query [2, 2]:

    • Color ball 2 with color 2.
    • Update cnt: Increment color 2 by 1. Now, cnt is {1: 1, 2: 1}.
    • Record that ball 2 has color 2 in g: g = {0: 1, 2: 2}.
    • There are 2 distinct colors, so append 2 to ans: ans = [1, 2].
  3. Query [0, 2]:

    • Change ball 0's color from 1 to 2.
    • 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}.
    • Update g to reflect ball 0's new color: g = {0: 2, 2: 2}.
    • Still only 1 distinct color, so append 1 to ans: ans = [1, 2, 1].
  4. Query [3, 1]:

    • Color ball 3 with color 1.
    • Update cnt: Increment color 1 by 1. Now, cnt is {2: 2, 1: 1}.
    • Record that ball 3 has color 1 in g: g = {0: 2, 2: 2, 3: 1}.
    • There are 2 distinct colors, so append 2 to ans: ans = [1, 2, 1, 2].

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.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Depth first search is equivalent to which of the tree traversal order?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More