A hash table is a data structure that maps keys to values for highly efficient lookup. There are a number of ways of implementing this. Here we describe a simple but common implementation

First compute the key’s hash code which will usually be an int or long . Note that two different keys could have the same hashcode, as there may be an infinite number of keys and a finite number of ints.

The map the hash code to an index in the array. This could be done with something like hash(key) % array_length. Two different hash codes could of course map to the same index

At this index, there is a linked list of keys and values. Store the key and value in this index. We must use a linked list because of collisions, you could have two different keys with the same has code, or two different hash codes that map to the same index

To retrieve the value pair by this key, you repeat this process. Compute the hash code from the key and the compute the index from the hash code. The, search through the linked list for the value with this key

If the number of collisions is very high,the worst case runtime id O(N), where N is the number of keys. However we generally assume a good implementation that keeps collisions to a minimum, in which case the lookup time is O(1)

A linked list is a data structure that represents a sequence of nodes. In a singly linked list, each node points to the next node in the linked list. A doubly linked list gives each node pointer to both the next node and previous node.

Unlike an array, a linked list does not provide constant time access to a particular index within the list. This means that if you’d like to find the Kth element in the list, you will need to iterate through K elements.

The benefits of a linked list is that you can add and remove items from the beginning of the list in constant time, For specific application that can be useful

The stack data structure is precisely what it sounds like, a stack of data. In certain types of problems, It can be favorable to store data in a stack rather than in an array

A stack uses LIFO (last in first out) ordering That is, as in a stack of dinner plates, the most recent item added to the stack is the first item to be removed

`pop()`

removes the top item from the stack

`push(item)`

add an item to the top of the stack

`peek()`

return the top of the stack

`isEmpty()`

return true if and only if the stack is empty

Unlike an array, a stack does not offer constant time access to the iths item. However it does allow constant time adds and removes as it doesn’t require shifting elements around

One case where stacks are often useful is in certain recursive algorithms, Sometimes you need to push temporary data onto a stack as you recurse, but the remove them as you backtrack (for example. Because the recursive check failed) A stack offers an intuitive way to do this.

A stack can also be used to implement a recursive algorithm Iteratively.

A queue implements a FIFO (first in first out) ordering. As in a line or queue at a ticket stand, items are removed from the data structure in the same order that they are added

It uses the operations

`add(item)`

: Add an item to the end of the list

`Remove()`

: Remove the first item in the list

`peek()`

: Return the top of the queue

`isEmpty()`

: Return true if and only if the queue is empty

It is especially easy to mess up the updating of the first and last nodes in a queue. Be sure to double check this.

One place where queues are often used is in breadth-first search or in implementing a cache

In breadth first search, for example we used a queue to store a list of the nodes that we need to process. Each time we process a node, we add its adjacent nodes to the back of the queue. THis allows us to process nodes in the order in which they are viewed.

Any interviews find tree and graph problems to be some of the trickiest. Searching a tree is more complicated

A nice way to understand a tree is recursive explanation. A tree is a data structure composed of nodes.

Each tree has a root node

The root node has zero or more child nodes

Each child node has zero or more child nodes

The tree cannot contain cycles. The nodes may or may not be in a particular order, they could have any data type as values and the may or may not have links back to their parent nodes

While there are a large number of recursive problems, many of them follow similar patterns. A good hint that a problem is recursive is that it can be built off of sub-problems.

When you hear a problem beginning with the following statement, it’s often (through not always) a good candidate for recursion.

“Design an algorithm to compute the nth…”

“Write code to list the first n”

“Implement a method to compute all...”

How to approach:

Recursive solutions, by definition are built off of solutions to sub-problems. Many times this will mean simply to compute f(n) by adding something, removing something or otherwise changing the solution for f(n-1). In other cases you might solve the problem for the first half of the data set, the the second half and the merge those results

There are many ways you might divide a problem into sub-problems. Three of the most common approaches to develop an algorithm are

`bottom up`

,

`top down`

,

`half half`

Although people make a big deal about how scary dynamic programming problems are, there’s really no need to be afraid of them. In fact once you get the hang of them, these can be very easy problems

Dynamic programming is mostly just a matter of taking a recursive algorithm and finding the overlapping sub-problems (that is, the repeated calls). You the cache those results for further recursive calls

Alternatively you can study the pattern of the recursive calls and implement something iterative. You still need to “cache” previous work

One of the simplest example of dynamic programming is computing the n'ths Fibonacci number. A good way to approach such a problem is often to implement it as a normal recursive solution and the add the caching part.