Beruflich Dokumente
Kultur Dokumente
Navigation:
parent(i) = floor (i/2), except for i = 1
left(i) = 2 * i, if left(i) > heapsize then left child no exist
right(i) = 2 * i + 1, if right(i) > heapsize then right child no exist
- Geometric Series
- Logarithmic Conversion
-
Table ADT
Operation Unsorted Array Sorted Array BBST �
search(v) O(N) O(logN) O(logN)
insert(v) O(1) O(N) O(logN)
findMax() O(N) O(1) O(logN)
listSorted() O(NlogN) O(N) O(N)
successor(v) O(N) O(logN) O(logN)
remove(v) O(N) O(N) O(logN)
getMedian() O(NlogN)* O(1) O(logN)
rank(v) O(NlogN)* O(logN) O(logN)
*Using QuickSelect in Tutorial 1, GetMedian() = Select(n/2). Expected O(n)
BST Property
For every vertex x and y,
y.key < x.key if y is in left subtree of x
y.key > x.key if y is in right subtree of x
Implementation
int array p
p[i] records the parent of i
if p[i] == i, then i is a root
initialise array p
and ArrayList of rank, with all initialised to 0
Graph Theory
Handshake Lemma: In an undirected graph, there must be an even number of odd-degree vertices.
In a complete graph,
o Thus, for a complete graph, O(E) = O(V2)
In a tree,
o Thus, in a tree, O(E) = O(V)
Complementary/Modified Algorithms
Reversed Path Keep backtracking via the predecessor array until you reach back to the
Reconstruction source, and printing as you visit the vertex.
(Iterative)
Path Reconstruction Recursively backtrack from the vertex into its predecessor until you hit the
(Recursive) root.
As you unfold the recursion, print the values which will be in order.
Check if u reachable from v BFS(v)/DFS(v) and check if visited[u] is true
Counting Components Component count initialise to 0, all vertices are unvisited.
O(V+E) Iterate through all the vertices,
If a vertex is unvisited, increment count by 1 and start DFS on the vertex.
Uses of BFS: (Finals AY2014/15 S1) Uses of DFS: (Finals AY2012/13 S1)
1. Graph traversal 1. Reachability test
2. Reachability Test 2. Find/Label/Count Components
3. Checking if the graph is connected. 3. Topological Sort of DAG.
4. Solving SSSP on an unweighted graph/weighted 4. Check if an undirected graph is a Bipartite
tree Graph
5. Checking if the graph is Bipartite. 5. Flood fill
6. Checking if a graph is a tree 6. Checking if graph is cyclic or acyclic
7. Finding articulation points/bridges
8. Finding strongly connected components in a
directed graph
Topological Ordering:
The linear ordering of the vertices in a DAG such that for every directed edge U-V, vertex U comes before
V in the ordering
Alternatively, each vertex comes before all vertices to which it has outbound edges.
Graph Theory
- A topological ordering is possible if and only if the graph is a DAG.
- A graph which has a cycle is not a DAG, does not have topological ordering.
o Every DAG has one or more topological sorts.
modifiedRecursiveDFSForToposort(vertex) {
isVisited[vertex] = true;
neighbours = AdjList.get(vertex);
for (v : neighbours) {
if (isVisited[v] == false) {
predecessor[v] = u;
recursiveDFS[v];
}
}
toposort.add(vertex); // post-order
}
Minimum Spanning Tree (MST) Problem
Finding the spanning tree with the least possible weight, of a connected undirected** weighted graph
MST Algorithms
Prim’s Algorithm Kruskal’s Algorithm
O(ElogV) O(ElogV)
PriorityQueue stores an edge (in the form of EdgeList which is sorted by increasing edge weight.
IntegerTriple of edge weight and the two incident UFDS to test if adding edge will cause a cycle.
vertices), and sorts by increasing edge weight.
T is an empty graph.
Pick a source vertex, and process* the source.
[While the PQ is not empty] [While there are unprocessed edges]
Dequeue the least weight edge, if the outgoing Get the least unprocessed weight edge
vertex is not visited, add this edge to the MST, If adding this edge to T does not cause a cycle,
and process* the outgoing vertex. add the edge.
Using existing MST Algorithm: Negate all weights in the graph and solve MST as per normal
SSSP Algorithms
Data Structures:
Predecessor array and distance array both of size V.
false corresponds to Negative Weight-Cycle Enqueue the source as [While PQ is not empty]
Infinity/Integer.MAX_VALUE Check* (0, source) and all Poll the front post
Reported if after the other vertices as pair.
Change algo, there is an edge (distance[v], v) into If the distance matches
visited[v] = true that can still be PriorityQueue which the distance array
Into relaxed. sorts in increasing (means, it’s not
distance[v] = distance. outdated), then relax
distance[u] + 1; all it neighbouring
OPTIMIZATION [While PQ is not empty] outgoing edges.
If weighted tree, then Add a flag, Dequeue the front most If an edge is relaxed,
remember to change 1 into If within an outerloop vertex and add the enqueue the new
weight(u,v) iteration, there is no vertex to solved. Relax (distance[v], v).
edge relaxation, stop all neighbouring
Simpler version the algorithm. Because outgoing edges. The old (distance[v], v)
Relax the neighbouring there is no edge that If an edge is relaxed, will be ignored because
edge by unit weight 1. needs to be relaxed update the distance of it does not match with
If an edge is relaxed, already. the outgoing vertex in the distance array,
enqueue it. the PQ.
Single Source Longest Path with all non +ve weight edges: Dikstra’s with Max PQ
Bottom-up DP:
From known base case, compute the solution to the larger problems using topological ordering of the DAG.
Top-down DP: Compute the solution to the largest sub problem by building a recurrence relation to it from
the solution to a smaller problem, making use of recursion and memoisation of answers to smaller sub
problems to avoid recomputations. In general, define the problem and define the recurrence, use
memoisation.
SSSP on a DAG
Bottom-up DP: O(V+E)
Topological sort the graph [O(V+E)]. Perform a one-pass Bellman Ford on a graph, which relaxes the
outgoing edges in topological order [O(V+E)]. A relaxation on the outgoing edges of a vertex makes use of
the fact that the incoming edges prior to it have been relaxed as well.
*Single-Source Longest Path on a general (cyclic) graph is NP-Hard and not in the scope of CS2010
Longest Increasing Subsequence
Bottom up DP: O(N2)
Model it as a (implicit) DAG which also includes a vertex of value infinity at the end of a subsequence.
(An edge exists from a low to a high number. And since there are N-1 edges in between N numbers, add a
dummy number of infinity to make the number of edges equals numbers in the sequence.)
Distance array is thus size N+1.
distance[i] is the length of the LIS ending at the vertex in A[i], all initialise to 0.
The array in itself is a topological order in a sense.
Answer: Infinity.
Single Source Longest Non Simple Path Problem on a General Graph with Cycles allows re-visitations, thus
causing cycles.
C cities, start at city S, can end at any other city E. But can only visit T number of cities.
What is the max profit he can get?
Top-down DP
You can model this as an SSLP problem on a DAG.
Change the graph into a DAG of vertex (City, NumCitiesLeft).
But an explicit graph modelling will have C*T number of vertices.
Let get_profit(u,t) be the maximum profit that salesman can get when he is at city u with t number of
cities left to visit.
get_profit(u,t) = 0 if t = 0,
get_profit(u,t) = -INF if cannot end at city u.
else if t > 0,
get_profit(u,t) = max(profit[u][v] + get_profit(v,t-1)) for all v in cities except for v = u.
Memoise using a 2D array memo table corresponding to city and NumCitiesLeft. O(C*T) space
Time Complexity
# of vertices in DAG - O(C*T)
Time to compute one distinct state (# of edges of each cities) - O(C)
Overall: O(C2*T)
Given C cities, completely connected to each other. Find the shortest tour, ending at the starting city,
which visits each of every other city exactly once.
DP taught in CS4243
On unweighted graph:
BFS – O(V*(V+E)) = O(V3) if E = O(V2)
Initialisation:
D[i][i] = 0
D[i][j] = edgeExist(i,j) ? weightOfEdge(i,j) : INF
Variants
Printing the Actual SP
Addition Data Structure:
2D predecessor matrix p[i][j] where p[i][j] is the predecessor of j on a shortest path from i to j.
Initialisation:
p[i][j] = i for all.
(Trace)
reconstructReversedPath(4, 3)
Modification:
i = 3
if (D[i][k] + D[k][j] < D[i][j]) {
j = 4
D[i][j] = D[i][k] + D[k][j];
j = 4 != 3; enter while loop
p[i][j] = p[k][j];
print(j) = print(4)
}
j = p[3][4] = 2
j = 2 != 2; remain in while loop
reconstructReversedPath(endVertex, source){
print(j) = print(2)
i = source; j = endVertex;
j = p[3][2] = 0
while (j != source) {
j = 0 != 2; remain in while loop
print(j);
print(j) = print(0)
j = p[i][j];
j = p[3][0] = 3
}
j = 3 == 3; break out of while loop
print(source);
print(source) = print(3);
}
Initialisation:
D[i][i] = 0
D[i][j] = 1 if edge i-j exists
D[i][j] = 0 otherwise
Modification:
D[i][j] = D[i][j] | (D[i][k] & D[k][j]);
Minimax/Maximin Problem
Finding the minimum of maximum edge weight along all possible paths from vertex I to vertex j (Minimax)
Finding the maximum of minimum edge weight along all possible paths from vertex I to vertex j (Maximin)
Initialisation:
D[i][i] = 0
D[i][j] = weight of edge i-j if it exists
D[i][j] = INF otherwise
Modification:
D[i][j] = Math.min(D[i][j], Math.max(D[i][k], D[k][j])); (Minimax)
D[i][j] = Math.max(D[i][j], Math.min(D[i][k], D[k][j])); (Maximin)
Modification:
Main diagonal of D to INF
After running Floyd Warshall’s recheck main diagonal
D[i][i] < INF but >= 0 +ve cycle
D[i][i] < 0 -ve cycle for vertex i
1) Merge routine on 2 lists/arrays already sorted. (which can also be used to find common element between
two lists/arrays)
Example:
list1: 1 2 3 5 7
list2: 0 4 6 7 10
---> 0 1 2 3 4 5 6 7 7 10
2) Quickselect to find kth smallest element in an unordered list, with random pivot
3) Counting Sort
O(n+k) where n is the number of elements in input array and k is the range of input.
- Counting sort is efficient if the range of input data is not significantly greater than the number of
objects to be sorted. Consider the situation where the input sequence is between range 1 to 10K and
the data is 10, 5, 10K, 5K.
- It is not a comparison based sorting. It running time complexity is O(n) with space proportional to
the range of data.
- It is often used as a sub-routine to another sorting algorithm like radix sort.
- Counting sort uses a partial hashing to count the occurrence of the data object in O(1).
- Counting sort can be extended to work for negative inputs also
4) Sorting Algorithms
Worst Best Average Notes
Insertion O(n2) O(n) O(n2)
Bubble O(n2) O(n) O(n2)
Selection O(n2) O(n2) O(n2) Unstable
Quick O(n2) O(nlog(n)) O(nlog(n)) Unstable
Merge O(nlog(n)) O(nlog(n)) O(nlog(n)) Not in-place
Heap O(nlog(n)) O(nlog(n)) O(nlog(n))
There are times when you would like to know a shortest distance from the end vertex to a particular
vertex. Or you would like to see a spanning tree from multiple vertices. Remember to change the graph to
exploit the algorithms you know.
Detailed Code Implementations
Binary Heap
// O(logN)
Insert(v)
heapsize++; // extend - O(1)
A[heapsize] = v; // insert at the back - O(1)
ShiftUp(heapsize); // fixes Binary Heap property - O(logN)
// ShiftUp in O(logN)
void ShiftUp(i)
while i > 1 && A[parent(i)] < A[i] // while not root and max heap property violated
swap(A[i], A[parent(i)])
i = parent(i)
// O(logN)
Obj ExtractMax()
maxV = A[1] // get the Max value
A[1] = A[heapsze] // replace the max with the last item
heapsize-- // decrease the heapsize
Shiftdown(1) // fix Binary Heap property – O(logN)
return maxV
// O(logN)
void shiftDown(int i)
while i <= heapsize
maxV = A[i];
maxIndex = i;
if (right(i) <= BinaryHeapSize && maxV < A[right(i)] // compare with right child, if exist
maxV = A[right(i)];
maxIndex = right(i);
if (max_id != i) {
swap(A[i] , A[maxIndex])
i = maxIndex;
else
break;
// O(NlogN)
CreateHeap(arr)
N = size(arr)
A[0] = 0 // dummy entry cos 1 based indexing
for (i = 1; i <= N ; i++)
Insert(arr[i-1]) // O(logN)
// O(N)
CreateHeap(arr)
N = size(arr)
A[0] = 0 // dummy entry
for (i = 1; i <= N; i++)
A[i] = arr[i-1]
for (i = parent(N); i = 1; i--) // from parent of last leaf down to the root
ShiftDown(i)
// O(NlogN)
Heapsort(arr)
CreateHeap(arr)
N = size(arr)
for (i = 1, i <= N; i++)
A[N-i+1] = ExtractMax()
return A
BST/AVLTree
// o(h)
search(v)
if (this.value == null) return null
else if this.value == v
return this.value
else if this.value < v
search right
else
search left
// o(h)
insert(v)
if insertion point is found
create new vertex
if v < vertex.value
go left
else
go right
inorder()
inorder(t.left)
print
inorder(t.right)
postorder()
postorder(t.left)
postorder(t.right)
print
// o(h)
delete(v)
find v in o(h) time
by comparing t.key to v
when v is found
if (t.left == null && t.right == null) // has no children, this is a leaf
t = null; // simply erase this node
t.right = w.left; // take the child on w's left and give it to t's right
if (w.left != null) // does this child even exist?
w.left.parent = t; // make the child know t is now it's parent
w.left = t; // *** t becomes w's left child
// update the vertex in the lower height then the other one.
w.size = t.size;
t.size = 1 + getsize(t.left) + getsize(t.right);
rightrotate(t) {
bstvertex w = t.left; // get the left child of t
w.parent = t.parent; // swap their parents by pointing w to t's parents
t.parent = w; // *** w becomes t's parent
t.left = w.right; // take the child on w's right and give it to t's left
if (w.right != null) // does this child even exist?
w.right.parent = t; // make the child know t is now it's parent
w.right = t; // *** t becomes w's right child
// update the vertex in the lower height then the other one.
w.size = t.size;
t.size = 1 + getsize(t.left) + getsize(t.right);
UDFS
findSet(i)
recursively visit p[i] until p[i] = i
everything visited will be connected to the representative item
isSameSet(i,j)
check findSet(i) and findSet(j) same representative item
unionSet(i, j)
general idea is
basically make the rep item of the taller tree as the rep item of the rep item of the shorter tree
if both trees are the same rank, then the i is nested under j and increase rank
// version 1
first you check whether they are same set.
then you find the rep item of i, and j, which we refer these rep items as x and y respectively
(aka x = findSet(i) and y = findSet(j))
if the rank of x is greater than y.
then nest y under x
else if the rank of y is greater than x
then nest x under y
else if the rank of them are the same.
then nest x under y
BUT increase the rank of y
// main loop
while (!queue.isEmpty) {
u = queue.dequeue();
neighboursOfU = AdjList.get(u);
for (v : neighboursOfU) {
if (isVisited[v] == false) {
isVisited[v] = true;
predecessor[v] = u;
queue.enqueue(v);
}
}
}
}
// O(V+E)
// Each vertex visited one, and flagged visited to avoid cycle
// (Assuming using AdjList) For each visited vertex, check all its k neighbours and visit them if
possible,
// Therefore, all E edges are examined.
// Therefore overall O(V+E)
DFS(source) {
for (i = 0; i < V; i++) {
isVisited[i] = false;
predecessor[i] = -1;
}
recursiveDFS(source);
}
recursiveDFS(vertex) {
isVisited[vertex] = true;
neighbours = AdjList.get(vertex);
for (v : neighbours) {
if (isVisited[v] == false) {
predecessor[v] = u;
recursiveDFS[v];
}
}
}
Graph Traversal Algorithms (Continued)
// iterative method
reconstructReversedPath(endVertex, source){
i = endVertex;
while (i != source) {
print(i); // Do whatever you want with i here, print, or grab the weight or whatever.
i = predecessor[i];
}
print(source);
}
// recursive method
reconstructPath(endVertex){
backtrack(endVertex);
}
void backtrack(vertex) {
if (vertex == -1) {
return
}
backtrack(predecessor[vertex]);
print(vertex); // by recursion, the order will be reversed to the correct order.
}
// check if u reachable from v
boolean checkReachability(v, u) {
BFS(v); // or DFS(v)
if (visited[u] == true) {
return true;
} else {
return false;
}
}
// O(V+E) Every vertex is still called at least once.
int countComponents() {
componentCount = 0;
for (i = 0; i < V; i++) {
isVisited[i] = false;
}
for (i = 0; i < V; i++) {
if (isVisited[i] == false) {
componentCount++;
recursiveDFS(i);
}
}
}
DFS() {
for (i = 0; i < V; i++) {
isVisited[i] = false;
predecessor[i] = -1;
}
toposort.clear();
for (i = 0; i < V; i++) {
if (isVisited[i] == false) {
modifiedRecursiveDFSForToposort(i);
}
}
outputInReversedOrder(toposort);
}
modifiedRecursiveDFSForToposort(vertex) {
isVisited[vertex] = true;
neighbours = AdjList.get(vertex);
for (v : neighbours) {
if (isVisited[v] == false) {
predecessor[v] = u;
recursiveDFS[v];
}
}
toposort.add(vertex); // post-order
}
MST Algorithms
// O(ElogV)
primsAlgo(source) {
addToMST(source);
enqueueAllEdgesConnectedToThisVertexIfNotInMST(source);
while (!pq.isEmpty()) {
edge = pq.dequeue(); //least weight edge in the PQ
v = getVertexLinkedToThisEdge(edge);
if (isPartOfMST(v) == false) {
addToMST(edge);
addToMST(v);
enqueueAllEdgesConnectedToThisVertexIfNotInMST(v);
}
}
}
enqueueAllEdgesConnectedToThisVertexIfNotInMST(vertex) {
neighbours = AdjList.get(vertex);
for (neighbour : neighbours) {
if (isPartOfMST(neighbour) == false) {
edge = getEdge(vertex, neighbour);
pq.enqueue(edge);
}
}
}
void addToMST(vertex) {
taken[V] = true;
}
void addToMST(edge) {
AdjList.add(edge);
}
boolean isPartOfMST(vertex) {
return taken[V];
}
// O(ElogV)
kruskalsAlgo() {
while (hasUnprocessedEdgesLeft() == true) {
edge = getMinimumUnprocessedEdgeFromSortedEdgeList();
if (createsCycleIfAddThisEdgeToMST(edge) == false) {
addToMST(edge);
} else {
continue;
}
}
}
boolean createsCycleIfAddThisEdgeToMST(edge) {
u , v = incident vertices of the edge
if (isSameSet(u,v)) return true;
else return false;
}
void addToMST(edge) {
u , v = incident vertices of the edge
unionSet(u,v)
}
SSSP Algorithms
initaliseSSSP(source) {
for (vertex : vertices) {
distance[vertex] = Integer.MAX_VALUE;
predeceessor[vertex] = -1;
}
distance[source] = 0;
}
boolean relax(u, v, weight(u,v)) {
if (distance[v] > distance[u] + weight(u,v)) {
distance[v] = distance[u] + weight(u,v);
predecessor[v] = u;
return true;
}
return false;
}
// For unweighted graph or all edges same weight
// Or on weighted tree (remember to modify the 1 into weight(u,v) respectively!)
// O(V+E) ; O(V) if weighted tree;
// version 1
modifieidBFSForSSSP(source) {
// initialisation
initaliseSSSP(source);
queue.enqueue(source);
// main loop
while (!queue.isEmpty) {
u = queue.dequeue();
neighboursOfU = AdjList.get(u);
for (v : neighboursOfU) {
if (distance[v] == Integer.MAX_VALUE) {
distance[v] = distance[u] + 1;
predecessor[v] = u;
queue.enqueue(v);
}
}
}
}
// Version 2 - simpler
modifieidBFSForSSSP(source) {
// initialisation
initaliseSSSP(source);
queue.enqueue(source);
// main loop
while (!queue.isEmpty) {
u = queue.dequeue();
neighboursOfU = AdjList.get(u);
for (v : neighboursOfU) {
canRelax = relax(u, v, 1);
if (canRelax) {
queue.enqueue(v);
}
}
}
}
int ans = 0;
for (int j = 0; i < AdjList.get(i).size(); j++){
int U = AdjList.get(i).get(j);
ans += numPaths(U);
}
memo.set(i, ans);
return ans;
}
private static int get_profit(int u, int t) {
if (t == 0) return candEnd[u] ? 0 : -INF?;
if (memo[u][t] != -1) return memo[u][t];
memo[u][t] = -INF;
for (int v = 0; v < C; v++) {
if (v == u) continue;
memo[u][t] = Math.max(memo[u][t], profit[u][v] + get_profit(v, t-1));
}
return memo[u][t];
}
LCS() {
for(i = 0; i <= n; i++)
D[i][0] = 0;
for(j = 0; i <= m; j++)
D[0][j] = 0;
for(i = 1; i <= n; i++) {
for(j = 1; j <= m; j++) {
if (x[i] === y[j])
D[i][j] = D[i-1][j-1] + 1;
else
D[i][j] = max(D[i-1][j], D[i][j-1]);
}
}
}