Python O(log(N)) Solution
IntuitionAnytime I see values and indexes, I always going with hashmapsApproachSo, we will be creating two hashmaps named indexes and numbers. The indexes hashm
# Intuition
Anytime I see values and indexes, I always going with hashmaps

# Approach
So, we will be creating two hashmaps named indexes and numbers. The indexes hashmap will have index as keys and numbers as values whereas numbers hashmap will have numbers as keys and the list of index as values. And the list of indexes will be implemented in heap memory. As when we push or pop a number in the list, it would be faster in TC.

In the change operation, we will add numbers and indexes to both the hashmaps. And in the find operation, if the number we are looking for is one of the keys in the Numbers, if not we will immediately return -1. If it is, then we will look for the first element in the allocated heap memory and match the index with the number we're looking for. 

If it's not, we should pop the index because that index is replaced by another number and look for the next elmt.
And, we will try to match the index with the number we're looking for and return the index.

# Example of the Operation:
["NumberContainers", "find", "change", "change", "change", "change", "find", "change", "find"]
[[], [10], [2, 10], [1, 10], [3, 10], [5, 10], [10], [1, 20], [10]]
-> 10 not in numbers, So we return -1.
After first change operation:
-> indexes = {2:10}
-> numbers = {10:[2]}

After second change operation:
-> indexes = {2:10, 1:10}
-> numbers = [10:[1,2]]

After third change operation:
-> indexes = {2:10, 1:10, 3:10}
-> numbers = [10:[1,2,3]]

After fourth change operation:
-> indexes = {2:10, 1:10, 3:10, 5:10}
-> numbers = [10:[1,2,3,5]]

Now, we have reached the find operation. As our Numbers hashmap, 10 is on many indexes but we can return only the first and unoccupied one. So, we return index 1.

Now, we change the 1 to 20 but we already have 1 as the key for 10. So, we change to 20.
-> indexes = {2:10, 1:20, 3:10, 5:10}
-> numbers = [10:[1,2,3,5]]

On the next find operation, we try to find the index of 10 and our Numbers hashmap says that its allocated to 20. So, we pop 1 from there as it belongs to 20 and 2 is the next index which belongs to 10. So, we return 2 as output.
-> indexes = {2:10, 1:20, 3:10, 5:10}
-> numbers = [10:[2,3,5]]

![Screenshot 2025-02-09 at 2.39.55 AM.png](https://assets.leetcode.com/users/images/6e58a81d-5416-471e-b769-2f71784d1a1f_1739049308.2406664.png)

# Complexity
- Time complexity:
O(log(n)) because we're pushing and popping from heap memory.

- Space complexity:
O(n) where n denotes the number of operations because the hashmaps may take the length of 'n' number of operations.
# Code
```python3 []
class NumberContainers:

    def __init__(self):
        self.indexes = {} #constructor as dictionary
        self.numbers = defaultdict(list)

    def change(self, index: int, number: int) -> None:
        self.indexes[index] = number
        heappush(self.numbers[number], index) #pushing the index

    def find(self, number: int) -> int:
        if number not in self.numbers: #checking if the number is present
            return -1

        while self.numbers[number]:
            index = self.numbers[number][0] #looking for the first elmt in heap

            if self.indexes[index] == number: #checking if the index we found is equal to number
                return index
            
            heappop(self.numbers[number]) #if not, we pop the index
        
        return -1 # if we can't find anything


# Your NumberContainers object will be instantiated and called as such:
# obj = NumberContainers()
# obj.change(index,number)
# param_2 = obj.find(number)
```
['Python3']