Stack
push(object) pop() top() size() isEmpty()Array
Use a int variable t to keep track of the index of the top. push(object) adds the input to the array[t+1] (1)
pop() removes the object at array[t] and returns it (1) top() returns the object at array[t] (1)
size() returns t+1 (1)
isEmpty() returns true if t is -1 (1)
Linked List
Use a node variable top to point to our last node.
push(object) adds the input to a node, makes the node point to the current top node, then makes the top variable point to this node. (1)
pop() saves the top’s object to a temporary node, makes the top variable point to the top’s next node then returns the temporary node. (1)
top() returns the top node’s object (1) size() returns t+1 (1)
isEmpty() returns true if top is null (1)
Queue
enqueue(object) dequeue() front() size() isEmpty()Array
Use two int variables f and r to track the front and rear. The rear index is the next empty entry. enqueue(object) inserts the input into a[r] then r = (r+1)%n (1)
dequeue() removes the object at a[f] and returns it then increments f to (f+1)%n (1) front() returns the object at a[f] (1)
isEmpty returns (f+1)%n=r (1)
Linked List
Use two node variables front and rear to store entry and exit positions. als int variables f and r for calculations.
enqueue(object) inserts the object at rear and increments r by 1 (1)
dequeue() removes the object at front and returns it and increments r by 1 (1) front() returns object at front (1)
size() returns (r-f+n)%n (1) isEmpty returns true if f=r (1)
Double-Ended Queues
Supports insertion and deletion from both the front and rear. addFirst(e) (1) removeFirst() (1) getFirst() (1) addLast(e) (1) removeLast() (1) getLast() (1) size() (1) isEmpty() (1)
3 & 7 - Vectors, Lists and Sequences
Vector (Array List)
get(i) returns an element at index i, error if empty
set(i,e) replace the element at index i with the input element, error if index i is empty
add(i,e) adds an element at index i, pushing all index>i by 1, error if index is isolated (no left and no right element)
remove(i) removes the element at index i, pushing all index>i by 1 to the left, error if empty size() returns number of elements
isEmpty() returns size()==0
Array
get(i) (1) set(i,e) (1) add(i,e) (n) remove(i) (n) size() (1) isEmpty() (1)If your array is full when add(i,e), you can replace the array with a bigger one. Incremental strategy increases size by a constant c
Doubling strategy doubles the size
List (Node List)
first() last() prev(p) next(p)
Use a head and tail node variables to keep track of list
set(p,e) replaces the element at position p to input element e, and returns the replaced element addFirst(e) adds an element at the head of the list and returns it
addLast(e) adds an element at the tail of the list and returns it addBefore(p,e) adds an element before position p and returns it addAfter(p,e) adds an element after position p and returns it getFirst()
getLast()
remove(p) remove element at position p and return it size()
isEmpty() returns getFirst() == null
Linked List
set(p,e) (1) addFirst(e) (1) addLast(e) (1) addBefore(p,e) (1) addAfter(p,e) (1) getFirst() (1) getLast() (1) remove(p) (1) size() (n) isEmpty() (1)Sequence (Doubly Linked List)
get(i) set(i,e) add(i,e) remove(i)
atIndex(i) returns position of element at index i first()
prev(p) next(p) set(p,e) addBefore(p,e) addAfter(p,e) addFirst(e) addLast(e) remove(p)
indexOf(p) returns index of element at position p size() isEmpty()
Array
get(i) (1) set(i,e) (1) add(i,e) (n) remove(i) (n) atIndex(i) (1) first() (1) last() (1) prev(p) (1) next(p) (1) set(p,e) (1) addBefore(p,e) (n) addAfter(p,e) (n) addFirst(e) (n) addLast(e) (1) remove(p) (n) indexOf(p) (1) size() (1) isEmpty() (1)Linked List
get(i) (n) set(i,e) (n) add(i,e) (n) remove(i) (n) atIndex(i) (n) first() (1) last() (1)prev(p) (1) next(p) (1) set(p,e) (1) addBefore(p,e) (1) addAfter(p,e) (1) addFirst(e) (1) addLast(e) (1) remove(p) (1) indexOf(p) (n) size() (1) isEmpty() (1)
Favorites List
acccess(e) returns the element and increments its access count remove(e) removes the element
top(k) returns the k top elements in terms of access count
8 - Trees
General Tree
Terminology
Root: no parent
Internal node: at least one child External node (leaf): no children Ancestor: all parents, grandparents, ... depth: number of ancestors
height: maximum depth
descendant: child, grandchild, ...
path: sequence of nodes between two nodes without cycle subtree: tree consisting of one node and descendants
Ordered Tree
A tree is ordered if there is a linear defining for each children (i.e. first child, second child, ...)
ADT
element() returns object at position (1) size() returns number of nodes (int) (1) isEmpty() returns size()==0 (boolean) (1) iterator() returns an iterator of all elements (n)
positions() returns an iterator of all nodes (n) root() returns the root node (1)
parent(p) returns the parent node (1)
children(p) returns an iterator of all children of p (c) isInternal() returns children(p)!=null
isExternal() returns children(p)==null isRoot() returns parent(p)==null
Tree Traversal Algorithms
Preorder
Algorithm preOrder(v) visit(v)
for each child w of v preOrder(w)
Postorder
Algorithm postOrder(v) for each child w of v
postOrder(w) visit(v)
Binary Tree
Binary trees have at most two children, a left and a right child. Proper tree nodes have either 2 or no children.
Properties for Proper Binary Trees
e= i + 1 n= e + i h≤ i h≤ (n - 1) /2 e≤ 2h h≥ log2e h≥ log2(n + 1) - 1
ADT methods
left(p) returns position of left child right(p) returns position of right child hasLeft(p) returns left(p)!=null hasRight(p) return right(p)!=null
Inorder
Algorithm inOrder(v) if hasLeft(v) inOrder(left(v)) visit(v) if hasRight(v) inOrder(right(v))Linked List
Node contains element variable, parent node, child(ren) node(s) size (1) isEmpty (1) iterator (n) positions (n) replace (1) root (1) parent (1) left (1) right (1) sibling (1) children (1) hasLeft (1) hasRight (1) isInternal (1) isExternal (1) isRoot (1) remove (1) insertLeft (1) insertRight (1) attach (1)
Array
Arrays contain elements in an index based order: index 1 is root
index 2*p is left child index 2*p+1 is right child size (1)
iterator (n) positions (n) replace (1) root (1) parent (1) left (1) right (1) sibling (1) children (1) hasLeft (1) hasRight (1) isInternal (1) isExternal (1) isRoot (1)
9 - Priority Queue and Heap
Priority Queue (when you need priority and order on entries)
Insertion is arbitrary, removal is ordered by keys. Each entry have a key and a value.
insert(k,v) removeMin() min()
size() isEmpty()
The Priority Queue contains an entry ADT with a key and a value and the following methods getKey()
getValue()
As well as a comparator ADT with the method
compare(a,b) which returns an integer i such that i<0 if a<b, i=0 if a=b and i>0 if a>b
Sorting
We can sort using a priority queue by inserting all the elements of a sequence into a priority queue and then reinserting those elements in order in a sequence with a number of removeMin() methods. Doing so depends on our implementation. Here are the two different sorts that may occur
Selection Sort (If the sequence is unsorted and is sorted during remove min
during removeMin())
Algorithm SelectSort(A) for i=0 to n-1 do minInd = i for j=i+1 to n-1 do if(A[j]<=A[minInd]) minInd=j end if end for temp = A[i] A[i] = A[minInd] A[minInd] = temp end forBest case = Worst case = On2
Insertion Sort (If the sequence is sorted at insertion)
Algorithm InsertSort(A) for i=1 to A.length-1
x = A[i] j = i-1 while (A[j] > x || j>-1) A[j+1] = A[j] j = j-1 end while A[j+1] = x end for
Best Case = O(n), Worst Case On2
Unsorted List
size(), isEmpty() (1) insert(e) (1) min() (n) removeMin() (n) remove(e) (1) replaceKey(e,k) (1) replaceValue(e,v) (1)Sorted List
size(), isEmpty() (1) insert(e) (n)min() (1) removeMin() (1) remove(e) (1) replaceKey(e,k) (n) replaceValue(e,v) (1)
Heap (When you need better time than O(n
2) for sorting algorithms)
Insertions and Removals are done in logarithmic time.
Complete Binary Tree
Heap order: key(v) ≥ key(parent(v))
You cannot have a right child without a left child.
You cannot add nodes to a lower depth if the previous one is not full. The last node is the leaf the closest to the right in the lowest depth. The minimum key is always at the root.
Insertion
You insert to the right of your last node. Then you perform the upheap to restore the heap order. Upheap is when you compare the node and its parent and swap them if the parent key is bigger than the node key
Algorithm Upheap(n)
while(n.parent != null && n.parent.key > n.key) swap n and n.parent
end while
Best Case O(1), Worst Case O(log2(n))
Removal
Removal from a heap (removeMin()) returns the root and replaces it by the last node. It then per-forms Downheap to restore heap-order.
Algorithm Downheap(n)
while(n.left != null && n.right !=null)
find smallest and swap n and smallest child end while
Best Case O(1), Worst Case O(log2(n))
Heap-Sort
It is performed by doing the operations of a priority queue sort but with a heap reducing the worst case to O(n log2(n))
Array
Bottom-up Heap Construction
If we know the number of elements in advance, we can construct a heap from the bottom up by inserting nodes in the depest level since we know it from the number of elements
(h ≥ log2(n + 1) - 1). You then enter 2h nodes and perform the necessary downheaps to restore the
order. This reduces the construction of the heap to O(n) instead of O(n log n)
Adaptable Priority Queue ADT
You can add remove(e), replaceKey(e,k) and replaceValue(e,v)
Heap
size(), isEmpty() (1) insert(e) (log n) min() (1) removeMin() (log n) remove(e) (log n) replaceKey(e,k) (log n) replaceValue(e,v) (1)10 - Map
Map
get(k) returns value of entry with key k or null (n)
put(k,v) enters value v at entry with key k return old value (n) remove(k) remove element with key k from map and return value (n) entrySet() return iterable collection of all key-value entries in map (n) keySet() return iterable collection of all keys in map (n)
values() return iterable collection of all values in map (n) size() return number of entries in map (1)
isEmpty() return size()==0 (1)
Ordered Map
firstEntry() smallest key or null (1) lastEntry() largest key or null (1)
floorEntry(k) largest key ≤ k or null (log n) ceilingEntry(k) smallest key ≥ k or null (log n)
Binary Search
Array
get, floorEntry and ceilingEntry take O(log n) with binary search
put and remove take O(n) because they require us to shift (at worst) n
2 entries
Dictionary ADT (List)
get(k) returns the entry with key k or null. If more than one, returns one randomly (n) getAll(k) returns an iterable collection of all entries with key k. (n^2)
put(k,v) inserts an entry with key k and value v (1) remove(e) removes the entry e and return it or error (n) entrySet() returns an iterable collection of entries (n) size(), isEmpty() (1)
Hash Table (When we need to access data instantly)
Hash function is a way to determine a value’s index with only its key. The logic is that since entries have specific keys, following the hash value of their key will take us to one specific index of a bucket where we can search for it lowering our search time.
If two entries have the same hash function result, we get collision.
Hash function
Typical hash function converts the key to a positive integer called the hash code and then com-presses it to fit the range of the hash table with a modulo n.
If your key is a string, use the unicode integer for each character and multiply them by a factor and sum them. To do that start with hash = 0 then hash = g * hash + s.charAt(i)
If your key is a polynomial, use a fixed value of x.
Compression functions
One of them is the MAD (multiply-add-and-divide)
(ay+b) mod N or even [(ay+b)mod p] mod N where p is a prime number prime numbers are good N
Collisions
You can either place a value in another location or make it so that one index can hold more than one entry.
Linear Probing
If the entry at hashTable[k] is already taken, use k+1, k+2 and so on. We need an available item to keep track of if we have ever inserted an item in specific entries.
Quadratic Probing
to keep track of if we have ever inserted an item in specific entries.
Double Hashing
If a first hashing results in an table entry already taken, use a second hashing function (different) to compute the increment. Thus we try h1(k) then h1(k) + h2(k) and h1(k) + 2 h2(k) and so on.
Separate Chaining (Solution to Open Adressing replacing all null by Available)
Every table entry represents a bucket that can hold more than one value. The bucket could be a list, sorted list, array, linked nodes, vector.
When collisions occur, the new value is added to the previous value by a pointer. It requires addi-tional memory outside the table.
get(k) returns the value of an entry with key k or null (n/N)
put(k,v) adds an entry at index of key k with value v and returns an old value or null (n/N) replaces old key k if already in.
remove(k) remove an entry with key k and return its value (n/N)
11 - Binary Search Tree and AVL Tree
Binary Search Tree
It is a tree such that if u is the left child t is the parent and w is the right child, then key(u) ≤ key(t) ≤ key(w). An inorder traversal visits the keys in increasing order. Searching consists of going to the right subtree log n times.
To insert you go through the tree as if searching and place the node where you should find it. To remove, if the key has one child, create a link from its parent to its child else if it has both, find the next inorder entry and replace it.
In rare cases the insertion can create a tree that looks like a regular linked list. The methods get, floorEntry, ceilingEntry, put and remove take O(h) but in those rare cases h in n. In lucky cases h is log(n)
AVL Tree (Avoid h = n)
An AVL Tree is a binary search tree such that the height of two children cannot differ by more than 1.
Insertion and removal work as in a binary search tree but requires restructuring to keep the balance between the nodes.
Restructuring is O(1) thus get (log n)
put (log n) remove (log n)
12 - Sorts
Other Sort
selection-sort (slow, in-place, <1K) On2
insertion-sort (slow, in-place, <1K) On2
heap-sort (fast, in-place, 1K-1M) O(n log n)
Merge Sort (fast, sequential data access for huge data sets (> 1M data))
Merge sort divides, conquers and combines. It separates an array into two sub arrays. It does so recursively until there is only one or two elements left, then it sorts them returning sorted subarrays. It then sorts the two subarrays together until it reaches the original array sorted. O(n log n)
Quick Sort (in-place, randomized, fastest (1K-1M))
The concept is the same as merge sort but we do not separate down the middle, rather we select a
random value and separate and order around it. Worst running case is On2 when we randomly
select the smallest or the biggest value every step. O(n log n)
Bucket Sort
We place the nodes in an array of buckets linearly O(n). We then return them to a regular array in order O(n + N). Thus the running time is O (n + N).
Radix Sort
We perform the bucket sort on all the different d-tuples of entries (if we sort by d variables)
O(d(n + N))
14 - Graphs
Graph - a set of vertices and edges Directed - edges have a direction
In-degree: number of edges going to the vertex Out-degree: number of edges going out the vertex
Strongly Connected: if there is a path for every two pair of vertices in the graph
Weakly Connected: if there is an undirected path for every two pair of vertices in the graph Complete: if there is an edge for every two pair of vertices in the graph
Undirected - edges represent both directions
Degree of a vertex: number of edges containing that vertex
Connected: if for every pair of vertices there is a path from one to the other Complete: if for every pair of vertices there is an edge from one to the other Self-edge: an edge that links a vertex to it self
graph
Simple Path: repeats no vertices except in a cycle fashion Cycle: a path where the first vertex is the same as the last vertex
Simple Cycle: Bot a cycle and a simple path Path Length: number of edges in a path
Path Cost: sum of weights in a path
Trees: an undirected, acyclic and connected graph
Direct Acyclic Graph (DAG): directed graph with no directed cycles
If the number of edges is OV2 the graph is dense
If the number of edges is O(V) the graph is sparse
Adjency Matrix (Best for dense graphs) (Space: V
2)
A matrix of size V× V of booleans that contains true in [u][v] if there is an edge from u to v To represent an undirected graph, the matrix will be symmetric around the diagonal
We can store numbers instead of booleans if we want weights get a vertex’s out-edges O(V)
get a vertex’s in-edges O(V) get an edge O(1)
insert an edge O(1) delete an edge O(1)
Adjency List (Good for sparse graphs) (Space: E + V)
An array of length V where we store which vertices we can go to (aka the edges) in a linked list fashion. (Bucket)
get a vertex’s out-edges O(d) get a vertex’s in-edges O(E) get an edge O(d)
insert an edge O(d) delete an edge O(d)
Graphs Traversal
Depth-First
Mark vertices as visited in a preorder fashion.
Breadth-First
You start with the first, then its neighbors, then their neighbors, etc. (Level-order)
Transitive Closure
Transitive Closure of a graph: is a graph that has all the edges and vertices of the original with extra vertices if there is a directed path from u to v then there must be an edge from u to v.
Floyd-Warshall’s Algorithm OV
3
At every vertex, find 2-edge connections with other vertices and add direct edges. Repeat until completion
Topological Sort
A list of nodes from a DAG where if there is a path from u to v, then u comes before v. To achieve this we use the postorder variant of depth-first which consists of visiting all outgoing edges until there are none left, then call recursively and then reversing it. If we have not visited all the vertices, select a nonvertex and DFS. This differs from preorder DFS because we might visit successors from lower level links.
Shortest Path
Dijkstra Algorithm O((V + E) log E)
From start node, visit nodes with smallest weight. Visit new nodes from adjacent nodes and add all weights, keep smallest.