LC-DesignQs
July 21, 2021
LC Design Qs
Design HashMap
- Why is it best to use a prime number as a mod in a hashing function?
-
There are two main issues that we should tackle, in order to design an efficient hashmap data structure: 1). hash function design and 2). collision handling. ```java // Space Complexity : O(N*K) ~ 10^4 * 10^2 // Time Complexity : O(n/k); keys/buckets // The idea is to reduce the space usage and the way we design // since as per the constraints the usage is at most 10^4 calls, so we can reduce the look up size by making 10^4(get,put,remove) * 10^2(lookups) class MyHashMap {
private static final int NUM_BUCKETS = 10000;//10^4 private Node[] buckets; private class Node{ int key, val; Node next;
Node(int key, int val){ this.key = key; this.val = val; } }
public MyHashMap() { buckets = new Node[NUM_BUCKETS]; }
public int getHash(int key){ return key%NUM_BUCKETS; }
// get you the previous node, always to do the operations of insert and delete public Node findPrev(int key){ int hash = getHash(key); Node node = buckets[hash]; // No list created if(node == null){ return null; }
Node prev = buckets[hash]; // dummy Node curr = prev.next; //find the node while(curr!=null && curr.key!=key){ prev = curr; curr = curr.next; } return prev;
}
public void put(int key, int value) { Node prev = findPrev(key);
// if no node exist if(prev == null){ prev = new Node(-1,-1); int hash = getHash(key); buckets[hash] = prev; } // if the key exists if(prev.next!=null){ prev.next.val = value; }else{ prev.next = new Node(key,value); } }
public int get(int key) { Node prev = findPrev(key); // No nodes exist or the key is null if(prev == null || prev.next == null){ return -1; }
return prev.next.val; }
public void remove(int key) { Node prev = findPrev(key); if(prev != null && prev.next != null){ prev.next = prev.next.next; } } }
#### Implement MinStack
```java
// Space Complexity : O(N) - using a copy of elemets
// Time Complexity : O(N) - moving and copying all elements from 1 stack to another.
class MinStack {
Stack<Integer> stack;
int minVal;
public MinStack() {
stack = new Stack();
minVal = Integer.MAX_VALUE;
}
// push the old min val if new min is encountered
public void push(int val) {
if(val<=minVal){
stack.push(minVal);
minVal = val;
}
stack.push(val);
}
// pop normally if the minVal != peek() else
public void pop() {
if(minVal == stack.peek()){
stack.pop();
minVal = stack.pop();
}else{
stack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minVal;
}
}
Implement Q using Stack
class MyQueue {
Stack<Integer> pushStack;
Stack<Integer> popStack;
public MyQueue() {
pushStack = new Stack<>();
popStack = new Stack<>();
}
public void push(int x) {
boolean isPush = true;
transferPushPop(isPush);
pushStack.push(x);
}
public int pop() {
boolean isPush = false;
transferPushPop(isPush);
return popStack.pop();
}
// Add to push stack, but before that see that popStack is Empty and all are transferred to the push stack.
// Before popping transfer from push to the pop, so that they are reversed.
public void transferPushPop(boolean isPush){
if(isPush){
while(!popStack.isEmpty()){
pushStack.push(popStack.pop());
}
}else{
while(!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
}
}
public int peek() {
boolean isPush = false;
transferPushPop(isPush);
return popStack.peek();
}
public boolean empty() {
return popStack.isEmpty() && pushStack.isEmpty();
}
}
Design HashSet
// Space Complexity : O(N*K) ~ 10^4 * 10^2
// Time Complexity : O(n/k); keys/buckets
class MyHashSet {
Object[] bucketArr;
private static final int H_KEY_SIZE = 10000;
public MyHashSet() {
// Design : 10^4 * 10^2 operations at a time so get faster access and reduce lookup
bucketArr = new Object[H_KEY_SIZE];
}
// Check if the key doesnot exist then add the value
public void add(int key) {
if(!contains(key)){
LinkedList<Integer> list = getList(key);
if(list==null){
bucketArr[getHash(key)] = new LinkedList<Integer>();
list = getList(key);
}
list.addFirst(new Integer(key));
}
}
// Get the linkedlist based on the hash value
public LinkedList<Integer> getList(int key){
int hash = getHash(key);
if(bucketArr[hash]==null){
return null;
}
LinkedList<Integer> list = (LinkedList<Integer>) bucketArr[hash];
return list;
}
// Storing based in groups like [1-100] if for %100
public int getHash(int key){
return key%H_KEY_SIZE;
}
// if list contains value remove
public void remove(int key) {
LinkedList<Integer> list = getList(key);
if(list!=null){
list.remove(new Integer(key));
}
}
// check if the list contains the value
public boolean contains(int key) {
LinkedList<Integer> list = getList(key);
// key does not exist
if(list==null){
return false;
}
//System.out.println(" List : "+list.toString());
// check if the list contains the key
return list.contains(new Integer(key));
}
}