Lesson 5

Greetings, aspiring coders! Today, we're going to delve deep into the complexities of data structures, specifically the ** TreeSet** from Java's Collection Framework, and explore how to handle queries efficiently. This is a common problem, often encountered in numerous data science and algorithmic problems. So let's gear up to unravel the mysteries of

`TreeSet`

operations and get our hands dirty with some interactive problem-solving!Before delving into the task, let's understand what a `TreeSet`

is and why we would use it. `TreeSet`

is a data structure in Java's Collection Framework that stores unique elements while maintaining sorted order.

Advantages of using `TreeSet`

:

- Extracting the minimum (using
`first()`

) or maximum (using`last()`

) values will be a constant-time operation, i.e., $O(1)$, as they are always at the start or end of the set. - Achieving sorted order after every insertion or deletion happens automatically with
`TreeSet`

, and the operations have a logarithmic time complexity $O(\log N)$.

Understanding these operations can help us utilize `TreeSet`

efficiently for our problem.

We are tasked with designing a Java function named `processQueries`

that can process a series of distinct requests or queries efficiently. The queries comprise a list of two integers — the type of operation and the operand.

There are three types of operations we'll handle:

- Adding an integer to the set (operation type 0)
- Removing an integer from the set (operation type 1). Whenever this operation is invoked, we can guarantee that the integer exists in the set.
- Finding the smallest integer that is greater than or equal to a given value (operation type 2).

The function should return the current size of the set when the operation type is 0 or 1, and the smallest possible integer when the operation type is 2. If such an integer does not exist, the function should return `-1`

.

Given a list of queries:

Java`1List<int[]> queries = Arrays.asList( 2 new int[]{0, 10}, 3 new int[]{2, 10}, 4 new int[]{0, 20}, 5 new int[]{1, 10}, 6 new int[]{2, 10} 7);`

The function should return: `[1, 10, 2, 1, 20]`

To start, we'll initialize our `TreeSet`

from Java's Collection Framework. We'll also create an empty `ArrayList`

labeled `results`

to store the outputs for each request.

Java`1import java.util.*; 2 3public class EfficientQueries { 4 public static List<Integer> processQueries(List<int[]> queries) { 5 TreeSet<Integer> sortedSet = new TreeSet<>(); 6 List<Integer> results = new ArrayList<>();`

Next, we utilize a `for`

loop to traverse through all the queries. For an operation type of 0 or 1, we either add or remove the provided value from our set. Subsequently, we append the size of the current set to `results`

.

Java`1import java.util.*; 2 3public class EfficientQueries { 4 public static List<Integer> processQueries(List<int[]> queries) { 5 TreeSet<Integer> sortedSet = new TreeSet<>(); 6 List<Integer> results = new ArrayList<>(); 7 8 for (int[] query : queries) { 9 int operation = query[0]; 10 int value = query[1]; 11 12 if (operation == 0) { 13 sortedSet.add(value); 14 } else if (operation == 1) { 15 sortedSet.remove(value); 16 } 17 18 results.add(sortedSet.size()); 19 } 20 return results; 21 } 22}`

Lastly, when the operation type is 2, we need to find the minimum bound, i.e., the smallest value greater than or equal to our provided value in the set. We perform this using the `TreeSet.ceiling`

operation. If such a value does not exist, we append `-1`

to `results`

.

Java`1import java.util.*; 2 3public class EfficientQueries { 4 public static List<Integer> processQueries(List<int[]> queries) { 5 TreeSet<Integer> sortedSet = new TreeSet<>(); 6 List<Integer> results = new ArrayList<>(); 7 8 for (int[] query : queries) { 9 int operation = query[0]; 10 int value = query[1]; 11 12 if (operation == 0) { 13 sortedSet.add(value); 14 results.add(sortedSet.size()); 15 } else if (operation == 1) { 16 sortedSet.remove(value); 17 results.add(sortedSet.size()); 18 } else if (operation == 2) { 19 Integer result = sortedSet.ceiling(value); 20 if (result != null) { 21 results.add(result); 22 } else { 23 results.add(-1); 24 } 25 } 26 } 27 return results; 28 } 29 30 public static void main(String[] args) { 31 List<int[]> queries = Arrays.asList( 32 new int[]{0, 10}, 33 new int[]{2, 10}, 34 new int[]{0, 20}, 35 new int[]{1, 10}, 36 new int[]{2, 10} 37 ); 38 39 List<Integer> result = processQueries(queries); 40 for (int res : result) { 41 System.out.print(res + " "); 42 } // Output: 1 10 2 1 20 43 } 44}`

Well done! You've successfully navigated the complexities of `TreeSet`

operations and developed an understanding of how to handle various types of queries efficiently using Java. Resolving the problem involved incorporating Java Collection Framework data structures, conditional statements, and utilizing methods like `ceiling`

within a sorted set.

The next step in your learning journey involves tackling similar challenges on your own using the concepts that you've just learned. Be sure to review this lesson as needed, and always remember: practice and apply these concepts. Happy coding!