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));
    }
}