Fully Explained | Python3 | Easy to Implement | For Beginners | O(log N) 
IntuitionWhen I first saw this problem, I thought of using two data structures to efficiently manage the relationship between indices and numbers. One to map in
# Intuition
<!-- Describe your first thoughts on how to solve this problem. -->
When I first saw this problem, I thought of using two data structures to efficiently manage the relationship between indices and numbers. One to map indices to numbers, and another to map numbers to a sorted list of indices. This would allow for quick updates and retrievals.
# Approach
<!-- Describe your approach to solving the problem. -->
The approach uses two main data structures:
index_to_number: A dictionary that maps each index to its corresponding number.
number_to_indices: A dictionary that maps each number to a sorted list of indices where it appears.

*For the change operation:*
1. If the index already exists, we remove it from the old number's list of indices.
2. We update the index_to_number mapping.
3. We add the index to the new number's list of indices.

*For the find operation:*
1. We check if the number exists in number_to_indices.
- If it does, we return the smallest index (which is the first element in the sorted list).
- If it doesn't, we return -1.

SortedList make sure that the list of indices for each number is always sorted, This is what will help us for O(log n) insertions and deletions.


# Complexity
- Time complexity:
<!-- Add your time complexity here, e.g. $$O(n)$$ -->
change: O(log n), where n is the number of indices for a particular number.
find: O(1) on average.
- Space complexity:
<!-- Add your space complexity here, e.g. $$O(n)$$ -->
O(n)
# Code
```python3 []
class NumberContainers:
    def __init__(self):
        self.index_to_number = {}
        self.number_to_indices = defaultdict(SortedList)

    def change(self, index: int, number: int) -> None:
        if index in self.index_to_number:
            old_number = self.index_to_number[index]
            self.number_to_indices[old_number].remove(index)
            
            if not self.number_to_indices[old_number]:
                del self.number_to_indices[old_number]
        
        self.index_to_number[index] = number
        self.number_to_indices[number].add(index)

    def find(self, number: int) -> int:
        if number not in self.number_to_indices or not self.number_to_indices[number]:
            return -1
        #print(self.number_to_indices)
        return self.number_to_indices[number][0]

```
['Array', 'Hash Table', 'Python3']