From 8d0ae49c0b9b6e38cd23c65e0b47c703217c32aa Mon Sep 17 00:00:00 2001 From: ankitabmungalpara Date: Sat, 25 Jan 2025 14:49:05 -0500 Subject: [PATCH 1/2] Create design-hashset.py --- design-hashset.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 design-hashset.py diff --git a/design-hashset.py b/design-hashset.py new file mode 100644 index 00000000..27349bfe --- /dev/null +++ b/design-hashset.py @@ -0,0 +1,98 @@ +""" + +Design a HashSet without using any built-in hash table libraries. + +Implement MyHashSet class: + +void add(key) Inserts the value key into the HashSet. +bool contains(key) Returns whether the value key exists in the HashSet or not. +void remove(key) Removes the value key in the HashSet. If key does not exist in the HashSet, do nothing. + + +Example 1: + +Input +["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"] +[[], [1], [2], [1], [3], [2], [2], [2], [2]] +Output +[null, null, null, true, false, null, true, null, false] + +Explanation +MyHashSet myHashSet = new MyHashSet(); +myHashSet.add(1); // set = [1] +myHashSet.add(2); // set = [1, 2] +myHashSet.contains(1); // return True +myHashSet.contains(3); // return False, (not found) +myHashSet.add(2); // set = [1, 2] +myHashSet.contains(2); // return True +myHashSet.remove(2); // set = [1] +myHashSet.contains(2); // return False, (already removed) + +Time Complexity: +- add(), remove(), and contains() operations all run in O(1) on average + since accessing and updating array elements takes constant time. + +Space Complexity: +- Worst-case space complexity is O(10^6) (for storing all possible keys up to 10^6). +- However, due to the lazy initialization of the secondary bucket arrays, + the average space usage is much lower. + +Did this code successfully run on Leetcode : Yes +Any problem you faced while coding this : No + +""" + +# Approach: +# used a two-dimensional array to simulate a hash set with separate chaining, +# where the first hash function determines the primary bucket and the second determines the secondary bucket. +# This minimizes memory usage while ensuring constant-time operations for add, remove, and contains. + +class MyHashSet: + + def __init__(self): + self.buckets = 1000 # primary + self.bucketItems = 1000 # secondary + self.storage = [None] * self.buckets + + + def hash1(self, key: int) -> int: + return key % self.buckets + + + def hash2(self, key: int) -> int: + return key // self.bucketItems + + + def add(self, key: int) -> None: + bucket = self.hash1(key) + bucketItem = self.hash2(key) + if self.storage[bucket] is None: + if bucket == 0: + self.storage[bucket] = [None] * (self.bucketItems + 1) + else: + self.storage[bucket] = [None] * self.bucketItems + self.storage[bucket][bucketItem] = True + + + def remove(self, key: int) -> None: + bucket = self.hash1(key) + bucketItem = self.hash2(key) + if self.storage[bucket] is None: + return + self.storage[bucket][bucketItem] = None + + + def contains(self, key: int) -> bool: + bucket = self.hash1(key) + bucketItem = self.hash2(key) + if self.storage[bucket] is None: + return False + return self.storage[bucket][bucketItem] is True + + +# Your MyHashSet object will be instantiated and called as such: +# obj = MyHashSet() +# obj.add(key) +# obj.remove(key) +# param_3 = obj.contains(key) + From f690b2da513fe3fd652854a488f5ec622e68dafb Mon Sep 17 00:00:00 2001 From: ankitabmungalpara Date: Sat, 25 Jan 2025 15:11:44 -0500 Subject: [PATCH 2/2] Create min-stack.py --- min-stack.py | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 min-stack.py diff --git a/min-stack.py b/min-stack.py new file mode 100644 index 00000000..f27e2fd4 --- /dev/null +++ b/min-stack.py @@ -0,0 +1,111 @@ +""" + +Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. + +Implement the MinStack class: + +MinStack() initializes the stack object. +void push(int val) pushes the element val onto the stack. +void pop() removes the element on the top of the stack. +int top() gets the top element of the stack. +int getMin() retrieves the minimum element in the stack. +You must implement a solution with O(1) time complexity for each function. + + +Example 1: + +Input +["MinStack","push","push","push","getMin","pop","top","getMin"] +[[],[-2],[0],[-3],[],[],[],[]] + +Output +[null,null,null,null,-3,null,0,-2] + +Explanation +MinStack minStack = new MinStack(); +minStack.push(-2); +minStack.push(0); +minStack.push(-3); +minStack.getMin(); // return -3 +minStack.pop(); +minStack.top(); // return 0 +minStack.getMin(); // return -2 + + +Method 1 +Time Complexity: O(1) for all operations +Space Complexity: O(n) due to maintaining an additional stack + +Method 2 +Time Complexity: O(1) for all operations +Space Complexity: O(n) since we store previous min values in the stack itself + +Did this code successfully run on Leetcode : Yes +Any problem you faced while coding this : No + +""" + +# Approach: +# Both methods use an auxiliary mechanism to track the minimum value efficiently. +# Method 1 maintains a separate minStack where each push stores the minimum seen so far, ensuring O(1) retrieval. +# Method 2 stores the previous min value in the stack itself whenever a new min is encountered, avoiding an extra list. + +# Method 1 +class MinStack: + def __init__(self): + self.stack = [] + self.minStack = [] + self.min = float("inf") + self.minStack.append(self.min) + + def push(self, val: int) -> None: + if val <= self.min: + self.min = val + self.stack.append(val) + self.minStack.append(self.min) + + def pop(self) -> None: + self.stack.pop() + self.minStack.pop() + self.min = self.minStack[-1] + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.min + + +# Method 2 +class MinStack: + def __init__(self): + self.stack = [] + self.min = float("inf") + + def push(self, val: int) -> None: + if val <= self.min: + self.stack.append(self.min) + self.min = val + self.stack.append(val) + + def pop(self) -> None: + val = self.stack.pop() + if val == self.min: + self.min = self.stack.pop() + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.min + + +# Your MinStack object will be instantiated and called as such: +# obj = MinStack() +# obj.push(val) +# obj.pop() +# param_3 = obj.top() +# param_4 = obj.getMin() + + +