CSE203 Notes by Promi
CSE203 Notes by Promi
Master Theorem:
T(n) = a T(n/b) + O (nd)
Case 1:
logba > d
⇒ T(n) = O(nlogba)
Case 2:
logba < d
⇒ T(n) = O(nd)
Case 3:
logba = d
⇒ T(n) = O(nd log n)
9
Master Theorem for dividing functions
T(n) = a T(n/b) + O(ndlogpn) [a>=1, b>1]
Case 1:
Case 2:
logba > d
⇒ T(n) = O(nlogba)
logba < d
⇒ T(n) = O(nd logp n)
07
when p >= 0
⇒
05
d
T(n) = O(n ) when p < 0
Case 3:
logba = d
⇒ T(n) = O(nd logp+1 n) when p > -1
⇒ d
T(n) = O(n log log n) when p = -1
⇒ T(n) = O(nd) when p < -1
20
2
LIST
Linked List based implementation
Singly linked list
Link class
Template <typename datatype> Class Link {
public:
Datatype value;
Link *next;
Link(const datatype &val, Link *next = null) {
this->value = val;
this->next = next;
}
9
Link (Link *next = null) { this->next = next; }
};
To iterate through whole list
O(n)
}
07
For ( LL.moveToStart(); LL.curPos() < LL.length(); LL.next() ) {
DataType val = LL.getValue();
// code
05
Singly linked list with freelist
Link Class
template<typename datatype> Class Link {
Private:
Static link<datatype> *freelist;
Public:
Datatype value;
20
Link *next;
Link (const datatype val, Link *next = null) {
this->value = val;
this->next = next;
}
Link (Link *next = null) { this->next = next; }
Void *operator new(size_t) {
If (freelist == null) Return new Link;
Link<datatype> *temp = freelist;
Freelist = freelist->next;
Return temp;
}
Void *operator delete (void *ptr) {
(Link<datatype>*)ptr->next = freelist;
3
freelist = (Link<datatype>*)ptr;
}
};
Template<typename datatype> Link<datatype> *Link<datatype> :: freelist = null;
Insertion and deletion, go to next
O(1)
Void insert(datatype val) {
cur->next = new Link<datatype> (val, cur->next);
If (cur == tail) { tail = tail->next; }
Length++;
}
Datatype delete() {
Assert (Current->next), “No element”;
Link<datatype> *temp = cur->next;
9
Datatype val = cur->next->value;
if (cur->next == tail) tail = cur;
current->next = current->next->next;
}
Delete temp;
length--;
return val;
void next() {
07
if (cur == tail) return;
05
cur = cur->next;
}
Accessing a specific position, go to previous
O(n)
void prev() {
if (cur == head) return;
20
Link<datatype>*temp = head;
while(temp->next != cur) temp = temp->next;
cur = temp;
}
void moveToPos(int pos) {
Assert (pos>=0 && pos<length), “Position out of range”;
cur = head;
for (int i = 0; i < pos; i++) cur = cur->next;
}
const datatype &getValue() const {
assert cur->next, “No value”;
return cur->next->value;
}
datatype getValue(int pos) {
moveToPos(pos);
4
return getValue();
}
9
insertion and deletion
O(1)
void insert(datatype val) {
}
datatype remove() {
07
cur->next = cur->next->prev = new Link(val, cur, cur->next);
length++;
Length--;
Return temp;
}
Append at last
O(1)
Void append(dataType val) {
Assert (length < capacity), “List capacity exceeded”;
Arr[length++] = val;
}
Move to any position, accessing item
O(1)
Void moveToStart() { curPos = 0; }
Void moveToEnd() {curPos = length; }
Void next() { if (curPos < length) curPos++; }
9
Void prev() { if (curPos>0) curPos--; }
Int length() { return length; }
Const datatype &getValue() const {
}
Void moveToPos(int pos) {
07
Assert (curPos>=0 && curPos<length), “No Current Element”;
return arr[curPos];
Stack
One array two stack
template<typename datatype> class twoStack {
public:
dataType *arr;
int capacity, top1, top2;
twoStack (int capacity) {
this->capacity = capacity;
arr = new dataType[capacity];
top1 = -1;
top2 = capacity;
}
9
void push1(datatype val) {
assert (top1>=-1 && top1<top2-1), “Not enough space”;
arr[++top] = val;
}
datatype pop1() {
}
07
assert (top1>=0 && top1<top2), “No element”;
return arr[top1--];
05
void push2 (datatype val) {
assert (top2<=capacity && top2>top1+1), “Not enough space”;
arr[--top2] = val;
}
datatype pop2() {
assert (top2<capacity && top2>top1), “No element”;
return arr[top2++];
20
}
int length1() { return top1+1; }
int length2() { return capacity-top2; }
};
9
TOHobj (int num, pole start, pole goal, pole temp) {
this->num = num;
this->start = start;
}
07
this->goal = goal;
this->temp = temp;
this->TOHop = DOTOH;
TOHobj *t;
while (s.length()) {
t = s.pop();
if (t->TOHop == DOMOVE) move(t->start, t->goal)
else if (t->num) {
s.push (new TOHobj(t->num-1, t->temp, t->goal, t->start));
s.push (new TOHobj(t->start, t->goal));
s.push (new TOHobj(t->num-1, t->start, t->temp, t->goal));
}
delete t;
}
}
8
Queue
Circular queue implementation
template <typename datatype> class Queue {
datatype *arr;
int capacity, front, rear;
public:
Queue (int capacity) {
this->capacity = capacity + 1;
this->front = 1;
this->rear = 0;
arr = new datatype[this->capacity];
}
9
void enqueue (datatype val) {
assert (front - rear + capacity)%capacity != 2, “Queue full”;
rear = (rear + 1)%capacity;
}
datatype dequeue () {
07
arr[rear] = val;
BST
Insertion
O(h)
⇒
h height of the tree ~ log n (for a balanced tree)
insert(node, val):
If node = null:
Node = new node
node.setValue(val)
Else:
If val > node.value:
Insert (node->right, val)
Else if val < node.value:
9
Insert (node->left, val)
Else:
Already there!!
Find minmum
O(h)
findMin(root):
If root->left is null:
07
05
Return root
Else:
findMin(root->left)
Searching
O(h)
search(root, val):
20
If root = null:
Return null
Else If val < root.value:
search(root->left, val)
Else if val > root.value:
search(root->right, val)
Else
Return root
Deletion
O(h)
delete(node):
If node is a leaf node:
Delete node
10
Inorder Traversal
O(n)
9
Inorder(node):
If node is null:
return
inorder(node->left)
Print node
inorder(node->right)
Preorder Traversal
O(n)
07
PreOrder(node):
05
If node is null:
return
Print node
preorder(node->left)
preorder(node->right)
Postorder Traversal
20
O(n)
postorder(node):
If node is null:
return
postorder(node->left)
postorder(node->right)
Print node
Level Order Traversal
O(n)
⇒
// there are n nodes, n-1 edges O(2n-1) ~ O(n)
LevelOrder(root):
// Applicable for all tree, not only binary tree; it’s like BFS
Define a queue Q
Q.enqueue(root)
While Q is not empty:
11
Temp = Q.dequeue()
Print temp
For every children i of temp:
Q.enqueue(i)
Height or depth
O(h)
depth(node):
If node is null:
Return 0
Return 1 + max{ depth(node->right), depth(node->right) }
Counting nodes
O(h)
9
count(root):
if root is null:
return 0;
07
return 1 + count(root->right) + count(root->left)
Graph
Graph LL implementation
Operations in O(V+E)
Class Graph {
Int nodeCount;
LL<int> vList[nodeCount];
Public:
Graph(int nodeCount) {
this->nodeCount = nodeCount;
For (int i=0; i < nodeCount; i++) {
vList[i] = new LL<int>(nodeCount);
}
9
}
Void insertEdge(int a, int b) {
vList[a].push(b); // assume directed graph
} 07
Void deleteNode(int a) {
For (int i = 0; i < nodeCount; i++) {
If (i != a) {
vList[i].erase(a);
05
}
}
Delete vList[a];
nodeCount–;
}
};
20
u = Q.dequeue()
For all neighbor v of u:
If v.visited = false:
V.parent = u
V.dist = u.dist + 1
V.visited = true
Q.enqueue(v)
9
V.parent = null
V.startTime = inf
v.endTime = inf
time = 0
V.visited = false
u.finishTime = ++time
9
Q.enqueue(root)
Root.color = red
While Q is not empty:
U = Q.dequeue()
07
For each neighbor v of u:
If v.color = null:
If u.color = red:
V.color = white
Else
05
V.color = red
Q.enqueue(v)
Else
If u.color = v.color:
Return false
Return true
20
9
Now G is in lexiographically smallest topological order
07
05
20
16
HEAP
Heapify function: (top to bottom)
O(log n)
I suppose this is minheap
Heapify1(node i):
If i == null:
Return
minNode = i
If i->right exists and i->right.value < i.value:
minNode = i->right
If i->left exists and i->left.value < minNode.value:
minNode = i->left
9
swap(minNode, i)
Heapify1(minNode)
Insertion
O(log n)
Insert(val):
17
Searching
O(n)
For each node i in the heap:
If i.value = val:
Return i
Deletion
O(n)
Delete(val):
Node i = search(val)
9
i.value = int_min (int_max for maxheap)
heapify2(i)
extractMin()
Sort:
O(n log n)
1. Declare a new arr[node_count]
2. Initialize i=0
20
3. Arr[i] = extractMin()
4. i --
5. Repeat step 3 and 4 till i = node_count
6. Arr has ascending order sorted values
Else ⇒
For i=0 to i=n/2 -2:
Check if both children of binTree[i] exist-
If not:
Return false
Check if (n/2-1)th node has only one child
If not:
Return false
Return true
isMinHeap(binTree):
If isComplete(binTree) is false:
Return false
For each node i (except root) in binTree:
If i < i->parent:
9
Return false
Return true
isMaxHeap(binTree):
07
If isComplete(binTree) is false:
Return false
For each node i (except root) in binTree:
If i > i->parent:
Return false
Return true
05
20
19
Greedy
Greedy Solution for Fractional Knapsack (Greedy)
O(n log n)
1. Calculate the value-per-pound ρi = vi/wi for i = 1, 2, . . . , n.
2. Sort the items by decreasing ρi .
Let the sorted item sequence be 1,2,...,i,...n, and the corresponding
value-per-pound and weight be ρi and wi respectively.
3. Let k be the current weight limit (Initially, k = K).
In each iteration, we choose item i from the head of the unselected list.
If k ≥ wi:
Set xi =1 (we take item i), and reduce k = k − wi, then consider the next
unselected item.
9
If k < wi:
Set xi = k/wi (we take a fraction k/wi of item i), Then the algorithm
terminates.
Correctness: 07
Given a set of n items {1, 2, ..., n}.
Assume items sorted by per-pound values: ρ1 ≥ ρ2 ≥ ... ≥ ρn.
Let the greedy solution be G = ⟨x1, x2, ..., xk ⟩
xi indicates fraction of item i taken (all xi = 1, except possibly for i = k).
05
Consider any optimal solution O = ⟨y1, y2, ..., yn⟩
yi indicates fraction of item i taken in O (for all i , 0 ≤ yi ≤1).
Knapsack must be full in both G and O:
sum(xiwi) = sum(yiwi) = K
Consider the first item i where the two selections differ.
By definition, solution G takes a greater amount of item i than solution O (because
the greedy solution always takes as much as it can).
20
Let x = xi − yi .
Consider the following new solution O′ constructed from O:
For j < i, keep yj′ = yj.
Set yi′ = xi .
In O, remove items of total weight xwi from items i + 1 to n, resetting the yj′
appropriately.
This is always doable because sumj=i to n(xj) = sumj=i to n(yj)
The total value of solution O′ is greater than or equal to the total value of solution O
Since O is largest possible solution
and value of O′ cannot be smaller than that of O,
O and O′ must be equal.
Thus solution O′ is also optimal.
20
By repeating this process, we will eventually convert O into G, without changing the
total value of the selection.
Therefore G is also optimal!
9
4. Put the job in this slot and mark this slot filled.
5. If no such i exists, then ignore the job.
●
21
Divide Conquer
Program to find k-th element from two sorted arrays: (Divide and conquer)
O(log n + log m)
int kth(int *arr1, int *arr2, int *end1, int *end2, int k):
if (arr1 == end1):
return arr2[k]
if (arr2 == end2):
return arr1[k]
int mid1 = (end1 - arr1) / 2;
int mid2 = (end2 - arr2) / 2;
if (mid1 + mid2 < k):
if (arr1[mid1] > arr2[mid2]):
9
return kth(arr1, arr2 + mid2 + 1, end1, end2, k - mid2 - 1)
else
return kth(arr1 + mid1 + 1, arr2, end1, end2, k - mid1 - 1)
else:
else
07
if (arr1[mid1] > arr2[mid2])
return kth(arr1, arr2, arr1 + mid1, end2, k)
else:
find x-median of the points
split P into L and R of about the same size using x-median
δ = min {MinDist(L), MinDist(R)}
B_L =points in L within δ of x median
B_R =points in R within δ of x median
sort BR by y-coordinates
For p∈BL do:
binary search for highest q∈BR in a δ×2δ box of p
compare at most 6 points starting with q to p
step 1: start
step 2: declare array and left, right, mid variable
step 3: perform merge function:
if left > right:
return
mid= (left+right)/2
mergesort(array, left, mid)
mergesort(array, mid+1, right)
merge(array, left, mid, right)
step 4: Stop
Pseudocode:
merge(A[1,2,...,k], B[1,2,...,l]):
If (A[1]<=B[1]):
A[1] o merge(A[2,3,...,k], B[1,2,...,l])
9
Else:
B[1] o merge(A[1,2,3,...,k], B[2,3,...,l])
mergeSort(A[1,2,3,...,n]):
07
merge(mergeSort(A[1,2,...,n/2]), mergeSort(A[n/2+1,...,n]))
Count += k - i
O.push(B[j])
J++
Else:
O.push(A[i])
i++
O.push(the only element left in A or B)
Return count
sortAndCount(Arr[0,1,2,...,n-1]):
If n==1:
Return 0
Divide Arr into two equal arrays A and B
R_a = sortAndCount(A)
R_b = sortAndCount(B)
9
Return r_a + r_b + mergeAndCount(A,B)
07
Integer Multiplication (Karatsuba) (Devide and conquer)
O(nlog3)
Input: Two integer numbers (both n digits) x and y
Output: xy (product)
1. Divide x into two parts a and b (where a*10n/2 + b = x)
2. Divide y into c and d (c*10n/2 + d = y)
05
3. xy = (a*10n/2 + b) * (c*10n/2 + d)
= ac*10n + (bc + ad)*10n/2 + bd
we need 4 smaller multiplications: ac, bd, bc, ad
⇒
T(n) = 4T(n/2) + O(n) T(n) = O(n2)
= ac*10n + (ac + bd - (a - b)(c - d))*10n/2 + bd
Now we need 3 smaller multiplications: ac, bd, (a-b)(c-d)
⇒
20
QuickSort
O(n log n)
quickSort(arr[], low, high):
if (low < high)
pi = partition(arr, low, high);
quickSort(arr, low, pi – 1);
quickSort(arr, pi + 1, high);
partition (arr[], low, high):
pivot = arr[high];
i = (low – 1)
24
for (j = low; j <= high-1; j++) → if (arr[j] < pivot) → swap arr[++i] and arr[j]
swap (arr[i + 1], arr[high])
return (i + 1)
9
rightSum = int_min
for i = n/2 to n:
sum += arr[i]
maxSum (A[1,2,...,n]):
if n=1:
return A[1]
07
rightSum = max(rightSum, sum)
return max(leftSum, rightSum, leftSum+rightSum-arr[n/2])
DP
0-1 knapsack (DP)
O(nw)
DP(k,w) is the optimal solution considering 1 to k items when the knapsack has
capacity w.
Base Case
DP(0,w) = 0 and DP(k,0) = 0
DP(k,w):
If wk>w: DP(k-1,w)
Else: max{Dp(k-1,w), vk+DP(k-1,w-wk)}
9
0-1 knapsack with unlimited supply (DP)
O(nw)
DP(k,w) is the optimal solution considering 1 to k items when the knapsack has
capacity w.
Base Case
DP(0,w) = 0 and DP(k,0) = 0
DP(k,w):
If wk>w: DP(k-1,w)
07
05
Else: max{Dp(k-1,w), vk+DP(k,w-wk)}
9
opt(k, 0) = 0
Lets define a nxm array where dp[i, j] means the cost of matching words A[1,2,...,i]
with B[1,2,...,j]
Base case:
DP[0, 0] = 0
DP[0, j] = j*ic
DP[i, 0] = i*ic
⇒
DP[i, j]
When A[i] = B[j]:
DP[i-1, j-1] + 0 (no cost)
When A[i] != B[j]:
Min { DP[i-1, j-1] + mc (substituition)
DP[i-1, j] + ic (delete A[i])
DP[i, j-1] + ic (insert A[j+1] = b[j]) }
Return DP[m][n]
9
07
05
20