Here are a list of questions that I use as a reference cheat sheet.
- Level order traversal
 - Inorder traversal
 - Post order traversal
 - Pre order traversal
 - Breadth First Search
 - Depth First Search
 - Implement Signly Linked-List Stack
 - Implement Signly Linked-List Queue
 - Implement Doubly Linked-List
 - Reverse a Linked-List
 - Remove node from a Linked-List
 - Insert at in a Linked-List
 - Remove duplicates from a Linked-List
 - Balance a BST
 - Binary Search
 - Merge Sort
 - Shuffle Array
 - Implement Comparator
 - Implement Hashmap
 - Stacks using queues
 - Queues using stacks
 - Validate a BST
 - Decimals to romans
 - Calculator using stacks
 - Fibonacci Series
 - String palindrome
 
At each node we print the left -> right
Worst case: O(n) - this happens when we have a one side leaning tree.
    public void levelOrderTraversal(Node n) {
        if (n == null) return;
        Queue<Node> qu = new Queue<>();
        qu.enque(n.root);
        while (!qu.isEmpty()) {
            Node current = qu.deque();
            System.out.print.out(current);
            if (current.left != null) {
                qu.enque(current.left);
            }
            if (current.right != null) {
                qu.enque(current.right);
            }
        }
    }At each node we print the left -> root -> right
Worst case: O(n) - this happens when we have a one side leaning tree.
    public void inOderTraversal(Node root) {
        if (root == null) return;
        Stack<Node> stack = new Stack<>();
        while (true) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            if (stack.isEmpty()) break;
            root = stack.pop();
            System.out.print(root.data);
            root = root.right;
        }
    }At each node we print the left -> right -> root
Worst case: O(n) - this happens when we have a one side leaning tree.
    public void postOrderTraversal(Node root) {
        if (root == null) return;
        Stack<Node> stack = new Stack<>();
        Stack<Node> stack2 = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node current = stack.pop();
            stack2.push(current);
            if (current.left != null) stack.push(current.left);
            if (current.right != null) stack.push(current.right);
        }
        while (!stack2.isEmpty()) {
            System.out.println(stack2.pop());
        }
    }At each node we print the root -> left -> right
Worst case: O(n) - this happens when we have a one side leaning tree.
    public void postOrderTraversal(Node root) {
        if (root == null) return;
        Stack<Node> stack = new Stack<>();
        
        while (true) {
            while (root != null) {
                System.out.print(root);
                stack.push(root);
                root = root.left;
            }
            if (!stack.isEmpty()) break;
            root = stack.pop();
            root = root.right;
        }
    }This algorithm is used to find items in a graph. At each level we search the left and right tree to see if we can find key Unlike trees, graphs can have cycles. We use the Queue data structure when implementing a queue
Worst case: O(E + V)
Best case: O(1)
    public boolean bfs(Node start, Node goalNode) {
        if (start == goal) return true;
       Queue<Node> queue = new LinkedList<>();
       ArrayList<Node> explored = new ArrayList<>();
       queue.add(start);
       explored.add(start);
       while (!queue.isEmpty()) {
           Node current = queue.deque();
           if (current == goalNode) return true;
           else if (current.getChildren().isEmpty(), explored) {
               return false;
           } else {
               queue.addAll(current.getChildren(), explored);
           }
           explored.add(current);
       }
       return false;
    }
    public ArrayList<Node> getChildren(Node n, ArrayList<Node> explored) {
        ArrayList<Node> result = new ArrayList<>();
        Node left = n.left;
        Node right = n.right;
        if (left != null && !expored.contains(left)) result.add(left);
        if (right != null && !expored.contains(right)) result.add(right);
        return result;
    }Another graph/tree travesal algorithm for finding items. We solve this by using a stack data structure
Worst case: O(n + m) - We get this complexity considering the fact that we are visiting each node only once and in the case of a tree (no cycles) we are crossing all the edges once.
Best case: O(1)
    public boolean dfs(Node startNode, Node goalNode) {
        if (startNode == goalNode) return true;
        Stack<Node> stack = new Stack<>();
        ArrayList<Node> visited = new ArrayList<>();
        while (!stack.isEmpty()) {
            Node current = stack.pop();
            if (current == goalNode) {
                visited.add(current);
                return true;
            } else {
                visited.add(current);
                node.addAll(curret.getChildren());
            }
        }
        return false;
    }Singly linked list is a list where the current node contains some data and a pointer to the next node. Travesal is only allowed in one way. This implementation is ideal for anyone who needs to use a stack
Complexity 
Instertion at head:  O(1) 
Remove at head:  O(1) 
Instertion at specified place:  O(n) 
Remove at specified place:  O(n) 
    public class LinkedList<T> {
        Node first = null;
        
        public void push(T item) {
            if (first == null) {
                first = new Node(item, null);
            } else {
                Node oldFirst = first;
                Node newFirst = new Node(item, oldFirst);
            }
        }
        public T pop() {
            if (first == null) throw new NoSuchElementException("Empty");
            T result = first.item;
            first = first.next;
            return result;
        }
    }
    public class Node<T> {
        T item;
        Node next;
        Node(T item, Node next) {
            this.T = T;
            this.next = next;
        }
    }Same as above with a subtle difference in the enque method
    public class LinkedList<T> {
        Node first = null;
        Node last = null;
        
        public void enque(T item) {
            Node oldLast = last;
            last = new Node(item, null);
            first = oldLast;
            if (first == null) first = last;
            else oldLast.next = last;
        }
        public T deque() {
            if (first == null) throw new NoSuchElementException("Empty");
            T result = first.item;
            first = first.next;
            return result;
        }
    }
    public class Node<T> {
        T item;
        Node next;
        Node(T item, Node next) {
            this.T = T;
            this.next = next;
        }
    }A doubly linked list is a type of that allows the insertion of data at the head and tail of the list.
    public class DoublyLikedList<T> {
        Node head;
        Node tail;
        public addFirst(T item) {
            Node tmp = new Node(item, head, null);
            if (head != null) {
                head.previous = tmp;
            }
            head = tmp;
            if (tail == null) {
                tail = tmp;
            }
        }
        public addLast(T item) {
            Node tmp = new Node(item, null, tail);
            if (tail != null) {
                tail.next = tmp;
            }
            tail = tmp;
            if (head == null) {
                head = tmp;
            }
        }
        public T removeFirst() {
            if (head == null) throw new NoSuchElementException();
            Node oldHead = head;
            head = head.next;
            head.previous = null;
            return oldHead.item;
        }
        public T removeLast() {
            if (tail == null) throw new NoSuchElementException();
            Node oldTail = tail;
            tail = tail.previous;
            tail.next = null;
            return oldTail.item;
        }
        public void iterateForward() {
            while (head != null) {
                System.out.print(head);
                head = head.next();
            }
        }
        public void iterateBackwards() {
            while (tail != null) {
                System.out.print(head);
                tail = head.previous();
            }
        }
    }
    public class Node<T> {
        Node next;
        Node previous;
        T item;
        
        Node(T item, Node next, Node previous) {
            this.T = T;
            this.previous = previous;
            this.next = next;
        }
    }Given A-B-C-D-E-F return F-E-D-C-B-A
Complexity: O(n)
    public void reverseList() {
        Node current = first;
        Node reversedList = null;
        while (current != null) {
            Node next = current.next;
            current.next = reversedList;
            reversedList = current;
            current = first;
        }
        first = reversedList;
    }Complexity: O(1)
    public void deleteNode(Node node) {
        node.item = node.next.item;
        node.next = node.next.next;
    }When we want to insert after a given node. If you are asked to insert after a position, you can just use a while loop and keep a counter
Complexity: O(1)
    public void insertAt(Node whereToInsert, Node newNode) {
        newNode.next = whereToInsert.next;
        whereToInsert.next = newNode;
    }The solution provided here is assuming that the list is sorted. If the list is un-sorted, you would need to have a Map and a new LinkedList to make it possible. The map would just contain list of things added to the new list
Complexity: O(n) where n is number of nodes in the given linked list.
    public void removeDuplicates(Node root) {
        while (root.next != null) {
            Node curret = root;
            Node next = root.next;
            if (current.item == next.item) {
                current.next = next.next;
            } else {
                current = current.next;
            }
        }
    }By definition a balanced tree is type of tree where the heights of the two child subtrees of any node differ by at most one
Insert, Search, delete: O(n log n)
    public void insertNode(Node node, int key) {
        if (node == null) {
            root = new Node(key);
        }
        if (key < node.key) {
            node.left = insertNode(node.left, key);
        } else if (key > node.key) {
            node.right = insertNode(node.right, key);
        }
        // Update node height
        node.height = 1 + Math.max(height(node.left), height(node.right));
        // Get the balance factor
        int balanceFactor = getBalanceFactor(node);
        // left left
        if (balance > 1 && key < node.left.key) rightRotate(node);
        // right right
        if (balance < -1 && key > node.right.key) leftRotate(node);
        // left right
        if (balance > 1 && key > node.left.key) {
            node.left = leftRotate(node.left);
            rightRotate(node);
        }
        // right left
        if (balance < -1 && key < node.right.key) {
            node.right = rightRotate(node.right);
            leftRotate(node);
        }
    }
    private int getBalanceFactor(Node n) {
        return n.left.height - n.right.height;
    }
    private void rightRotate(Node node) {
        Node left = node.left;
        Node T2 = node.right;
        // Perform rotations
        right.right = node;
        node.left = T2;
        node.height = Math.max(node.left.height, node.right.height);
        left.height = Math.max(left.left.height, left.right.height);
    }
    private void leftRotate(Node node) {
        Node y = node.right;
        Node T2 = y.left;
        // Perform rotation
        y.left = node;
        node.right = T2;
        // Update heights
        node.height = Math.max(node.left.height, node.right.height) + 1;
        y.height = Math.max(height(y.left), height(y.right)) + 1;
    }Uses a divide and conquer approach to search a given list
Complexity: O(log n)
    public boolean binarySearch(int[] data, int n) {
        int lo = 0;
        int hi = data.length;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (n < data[mid]) hi = mid - 1
            else if (m > data[mid]) lo = mid + 1;
            else return true;
        }
        return false
    }Sort left half, followed by sorting of right half and then merge the two sorted list together
Complexity: O(n log n)
    TBDWe can shuffle an array using the Fisher Yates & Knuth shuffling algorithm in O(n) time. At each ith iteration we generate a random number from the start of the array to i and then do the swap
Complexity: O(n)
    public void shuffle(int[] array) {
        for (int i = 0; i < array.length; i++) {
            // choose index uniformly in [0, i]
            int randomNumber = i + Math.random() * (i + 1);
            // perform the swap
            int temp = a[i];
            a[i] = a[randomNumber];
            a[randomNumber] = temp;
        }
    }The task at hand is to convert a given decimal number n say 20 to its roman numeral equivalence.
See here for chart