#115 π¦ 2349. Design a Number Container System ππ§
Imagine you have a system where you can store numbers at specific indices and quickly retrieve the smallest index containing a particular number. Sounds easy, right? π€ But the challenge lies in making it efficient and scalable since the index and number range can go up to 10βΉ, and we can have 100,000 operations!
In this post, weβll break down this problem step by step:
β
Understanding the problem statement π
β
Building a naive solution π οΈ
β
Optimizing it for efficiency β‘
β
Time complexity analysis β³
β
Example walkthrough with large numbers π’
β
Edge cases to consider π§
β
Conclusion π―
Letβs get started! π
π Problem Statement
We need to implement a Number Container System that supports two operations:
1οΈβ£ change(index, number)
: Stores or updates the number at a given index. If a number already exists at the index, it is replaced.
2οΈβ£ find(number)
: Returns the smallest index containing the given number. If the number is not found, return -1.
πΉ Example
Input:
["NumberContainers", "find", "change", "change", "change", "change", "find", "change", "find"]
[[], [10], [2, 10], [1, 10], [3, 10], [5, 10], [10], [1, 20], [10]]
Output:
[None, -1, None, None, None, None, 1, None, 2]
Explanation:
find(10) -> -1
(No numbers stored yet π«)change(2, 10)
,change(1, 10)
,change(3, 10)
,change(5, 10)
(Stores10
at indices 2, 1, 3, 5)find(10) -> 1
(The smallest index with10
is 1)change(1, 20)
(Replaces10
at index 1 with20
)find(10) -> 2
(The smallest index with10
is now 2)
π οΈ Basic Solution (Brute Force)
π Idea
Weβll use a dictionary (hash map) to store index β number mappings. To find the smallest index for a given number, we scan all entries in the dictionary.
πΉ Implementation
class NumberContainers:
def __init__(self):
self.indexToNum = {}
def change(self, index: int, number: int) -> None:
self.indexToNum[index] = number
def find(self, number: int) -> int:
min_index = float('inf')
for idx, num in self.indexToNum.items():
if num == number:
min_index = min(min_index, idx)
return min_index if min_index != float('inf') else -1
β³ Time Complexity
change(index, number)
: O(1) β (Dictionary update)find(number)
: O(N) β (We scan all indices)
This approach fails for large inputs, where find()
runs in O(N), making it too slow! π¨
β‘ Optimized Solution (Using Min Heap)
π Idea
Instead of scanning all indices, we maintain a min-heap (priority queue) for each number to quickly retrieve the smallest index.
πΉ Approach
1οΈβ£ Use two dictionaries:
indexToNum
: Maps each index to the number stored there.numToIndices
: Maps each number to a min-heap of indices where it appears.
2οΈβ£ change(index, number)
:
- Update
indexToNum
. - Add
index
to the min-heap fornumber
.
3οΈβ£ find(number)
:
- Retrieve the smallest index from the min-heap.
- If it points to a different number (due to updates), remove it and check again.
πΉ Implementation
import heapq
class NumberContainers:
def __init__(self):
self.indexToNum = {} # Maps index -> number
self.numToIndices = {} # Maps number -> min heap of indices
def change(self, index: int, number: int) -> None:
self.indexToNum[index] = number
if number not in self.numToIndices:
self.numToIndices[number] = []
heapq.heappush(self.numToIndices[number], index)
def find(self, number: int) -> int:
if number not in self.numToIndices:
return -1
pq = self.numToIndices[number]
while pq:
currIndex = pq[0]
if self.indexToNum[currIndex] != number:
heapq.heappop(pq) # Remove outdated index
else:
return currIndex
return -1
β³ Time Complexity
change(index, number)
: O(log N) (Heap push) βfind(number)
: O(log N) (Heap operations) β
This is much faster than the brute force O(N) solution! π
π Example Walkthrough (Large Numbers)
πΉ Input
nc = NumberContainers()
nc.change(1000000000, 99) # Index 1B -> 99
nc.change(999999999, 99) # Index 999M -> 99
nc.change(500000000, 99) # Index 500M -> 99
nc.find(99) # Expected: 500M (Smallest index)
nc.change(500000000, 100) # Replace 99 with 100 at 500M
nc.find(99) # Expected: 999M (Smallest index left with 99)
πΉ Execution
change(1000000000, 99) β
change(999999999, 99) β
change(500000000, 99) β
find(99) -> 500000000 β
change(500000000, 100) β
find(99) -> 999999999 β
π§ Edge Cases
β
Large index values (Test with 10βΉ
values)
β
Updating an existing index (change()
should replace values correctly)
β
Removing the smallest index (Ensure find()
updates correctly)
β
Empty system (find()
should return -1
)
π― Conclusion
π― The naive approach (O(N)) was too slow for large inputs.
β‘ The optimized heap-based approach (O(log N)) is much faster.
π οΈ We used two dictionaries + min-heap to efficiently track indices.
This problem teaches hash maps, heaps, and efficient data lookupsβessential for competitive programming and system design! π