Sorting
Insertion sort \(O\)(n2)
Merge sort \(O\)(n log n) by divide and conquer
Analyze complexity of recursions
By expansion: the recursion tree method
By induction: the substitution method
In-place sort - sorted item occupy the same storage as the original one
Out-of-space sort - sorting algo used extra space to do sorting
INSERTIONSORT(A)
START
FOR i = 1 to n
WHILE A[i] < A [i -1]
swap A[i] with A[i-1]
i -= 1
ENDWHILE
ENDFOR
END
SWAP(A, i)
START
temp <-- A[i]
A[i] <-- A[i-1]
A[i-1] <-- temp
END
Worst-case running time \(T\)(n) on an input of size n
\(T\)(n) = \(\frac{n(n-1)}{2}\) = \(\Theta\)(n2)
Divide and conquer solution
- Divide input into parts (smaller problems)
- Conquer (solve) each part recursively
- Combine results to obtain solution of original
\(T\)(n) = divide time [\(\Theta\)(n)]
T(n1) + T(n2) + … + T(nk) [2\(T\)(n/2)]
combine time [\(\Theta\)(n)]
\(T\)(n) = 2\(T\)(n/2) + \(\Theta\)(n)
MergeSort(A)
START
IF n = 1 THEN
done -> return
ENDIF
RECURSIVE_SORT A[:n/2] --> L
RECURSIVE_SORT A[n/2:] --> R
merge L & R --> output A'
END
MERGESORT(a)
START
IF n == 1 THEN
RETURN a
l1 <-- a[0] ... a[n/2]
l2 <-- a[n/2+1] ... a[n]
l1 <-- call: MERGESORT(l1)
l2 <-- call: MERGESORT(l2)
RETURN MERGE(l1, l2)
END
MERGE(a, b)
START
c <-- []
WHILE a and b have elements
IF a[0] > b[0] THEN
add b[0] to the end of c
remove b[0] from b
ELSE
add a[0] to the end of c
remove a[0] from a
ENDIF
ENDWHILE
WHILE a has elements
add a[0] to the end of c
remove a[0] from a
ENDWHILE
WHILE b has elements
add b[0] to the end of c
remove b[0] from b
ENDWHILE
RETURN c
END
The recursion tree = sum of cost of nodes
\(T\)(n) = 2\(T\)(n/2) + cn
Sum of each level and then sum all levels \(T\)(n) = n\(T\)(1) + cn log n \(\Theta\)(nlogn)
\(T\)(n) = \(T\)(n/2) + cn
\(T\)(n) = cn + cn/2 + cn/4 + …. + 1 = cn (1 + ½ + ¼ + …. + 1/2logn) = cn (2) = Θ(n)
Sum of each value in each level is the same, Total value = \(\Theta\)(no. of levels * sum of each lvl)
Each level’s value is decreasing, Total value = \(\Theta\)(root node’s value)
Each level’s value is increasing, Total value = \(\Theta\)(no. of leaves)
\(T\)(n) = a \(T\)(n / b) + f(n)
No. of lvl = logb(n) = \(\Theta\)(log n)
No. of leaves = alogb(n) = nlogb(a)
value of each lvl is geometrically increasing down the tree
if f(n) = \(O\)(L1-\(\epsilon\)) = \(O\)(nlogb(a)-\(\epsilon\))
\(T\)(n) = \(\Theta\)(L) = \(\Theta\)(nlogb(a))
Note: dominant no. of leaves
log(n) –> log(n/2) –> log(n/4) –> … –> log(n / 2k) –> c
No. of leaves increasing down the tree (dominant)
f(n) = \(O\)(L1-\(\epsilon\)) = \(O\)(nlogb(a)-\(\epsilon\))
T(n) = \(\Theta\)(nlogb(a)) = \(\Theta\)(n)
value of each lvl are equal Height \(\times\) Leaves
f(n) = \(\Theta\)(L) = \(\Theta\)(nlogb(a)) T(n) = \(\Theta\)(L log n) = \(\Theta\)(n(logb(a) log(n)) = \(\Theta\)(n log n), a = b = 1
n –> n/2 –> n/4 –> … –> n/2k –> c
T(n) = \(\Theta\)(L log n) = \(\Theta\)(nlogb(a) log n)
value of each is lvl is geometrically decreasing down the tree
f(n) = \(\Omega\)(L1+\(\epsilon\)) = \(\Omega\)(n(logb(a)+\(\epsilon\))
\(T\)(n) = \(\Theta\)(f(n))
Note: dominant node
n2 –> (n/2)2 –> (n/4)2 –> … –> (n/2k)2 –> c
n2 is significantly bigger than n log n
\(T\)(n) = 9\(T\)(n/3) + n
a = 9, b = 3, f(n) = n
height = log3(n); leaves = 9^log3(n) = nlog3(9) = \(\Theta\)(n2)
Note: height = logb(n) (bheight = n), #leaves = alogb(n) = nlogb(a)
Since f(n) = \(O\)(nlog3(9-\(\epsilon\))) where \(\epsilon\) = 1 \(T\)(n) = \(\Theta\)(#leaves) = \(\Theta\)(n2)
\(T\)(n) = \(T\)(2n/3) + \(\Theta\)(1)
a = 1, b = 3/2, f(n) = 1
height = log(3/2)(n); leaves = log3/2(n) = nlog3/2(1) = n0 = \(\Theta\)(1)
f(n) = \(\Theta\)(nlog(3/2)(1))
\(T\)(n) = \(\Theta\)(nlog(3/2)(1) log n = \(\Theta\)(log n)
\(T\)(n) = 3\(T\)(n/4) + n log n
a = 3, b = 4, f(n) = n log n
height = log4(n); #leaves = 3log4(nlogn) = nlog4(3) \(\approx\) n0.793
f(n) = \(\Omega\)(nlog4(3+\(\epsilon\))) where \(\epsilon\) 0.2
AND af(n/b) = 3f(n/4) = 3/4n log(n/4) <= cn log n, for c = 3/4 (regularity condition)
check if value is decreasing, if c < 1 and cn log n dominates 3/4n log(n/4)
\(T\)(n) = \(\Theta\)(f(n)) = \(\Theta\)(n log n)
\(T\)(n) = 4\(T\)(n/2) + n2
a = 4, b = 2, f(n) = n2
height = log2(n), #leaves = 4log2(n) = nlog2(4) = n2
f(n) = \(\Theta\)(n2)
\(T\)(n) = \(\Theta\)(n2 log n)
\(T\)(n) = 7\(T\)(n/3) + \(\Theta\)(n2)
a = 7, b = 3, f(n) = n2
height = log3(n), #leaves = 7log3(n) = nlog3(7)
f(n) = \(\Omega\)(nlog3(7+\(\epsilon\))), where \(\epsilon\) \(\approx\) 0.229
Since af(n/b) = 7f(n/3) = 7(n/3)2 = 7/9n2 <= cn2, for c = 7/9 (regularity condition)
\(T\)(n) = \(\Theta\)(n2)
\(T\)(n) = \(T\)(n - 1) + 2
2 –> 2 –> … –> 2
n –> n-1 –> 1 2(n); \(T\)(n) = \(\Theta\)(n)
Peak Finding Problem
Neighbors < Peak element
10, 13, 5, 8, 3, 2, 1
PEAKFINDER(A)
START
IF A.length == 0 THEN //If list is 0, there is no peak
return NIL
ENDIF
IF A.length == 1 THEN // If list has been reduced to 1 element, it's a peak
return A
ENDIF
mid = A.length / 2
left = mid - 1
right = mid + 1
IF A[left] <= A[mid] >= A[right] THEN
RETURN A[mid]
ELSE IF A[mid] < A[left]
RETURN PEAKFINDER(A[:mid])
ELSE IF A[mid] < A[right]
RETURN PEAKFINDER(A[mid+1:])
ENDIF
END
An element A[i] is a peak if it is not smaller than all its neighbor(s)
IF i != 1 THEN
n <-- A[1] >= A[i - 1] and A[i] >= A[i + 1]
ELSE IF i = 1
A[1] >= A[2]
ELSE IF i = n
A[n] >= A[n-1]
ENDIF
13 & 8 are peaks
Algo 1
Scan the array fr left to right
Compare A[i] with its neighbors
Exit when found a peak
1, 2, 4, 8, 9, 12, 21
Complexity:
Might need to scan all elements, so \(T\)(n) = \(\Theta\)(n)
Algo 2
Consider the middle element of the array & compare with neighbors
IF A[n/2-1] > A[n/2] THEN
search for a peak among A[1]... A[n/2 - 1]
ELSE IF A[n/2] < A[n/2 + 1]
search for a peak among A[n/2 + 1]...A[n]
ELSE
A[n/2] is a peak! //since A[n/2 -1] <= A[n/2] and A[n/2] >= A[n/2 + 1]
ENDIF
Complexity
\(T\)(n) = \(T\)(n/2) + \(O\)(1) //Time for comparing A[n/2] with 2 neighbors
\(T\)(n) <= \(T\)(n/2) + c <= (\(T\)(n/22) + c) + c <= … <= \(T\)(n/2log2(n)) + c + c + … + c = 1 + log2(n) = \(O\)(log n)
Time to find peak in array of length n, O(1) time to find peak, \(T\)(n/2) cut array in half
Divide and Conquer
Very powerful design tool:
Divide input into multiple disjoint parts
Conquer each of the parts separately (using recursive call)
Peak finding: 2D
Consider a square 2D array A[1…n, 1…n]:
An element A[i] is a 2D peak if it is not smaller than its (at most 4 neighbors.
Problem: find any 2D peak
Algo 1: brute-force method (i.e. search all squares)
Complexity = \(\Theta\)(n2)
Algo 2:
a) For each col j, find global maximum B[j]: complexity = \(\Theta\)(n)
b) Apply 1D peak finder to find a peak of B[1…n] (say B[j]): complexity = \(\Theta\)(n2 + log n), log n to
find the peak
Complexity = \(\Theta\)(n2 _ log n) = \(\Theta\)(n2)
Algo 3:
Pick middle col j = n/2<br>
Find global max on col j, [i, j]<br>
Compare A[i, n/2] to A[i, n/2 - 1] A[i, n/2 + 1]<br>
IF A[i, n/2] < A[i, n/2-1] THEN
pick left col j=[1...n/2]
ELSE IF A[i, n/2] < A[i, n/2+1]
pick right col j = [n/2...n]
ELSE IF A[i, n/2] >= A[i, n/2 -1] AND A[i, n/2] >= A[i, n/2+1]
A[i, j] is 2D peak
PEAKFINDER(A, rows, cols, mid)
START
max <-- 0
max, max_index <-- call: FINDMAX(A, rows, mid, max)
IF mid == 0 OR mid == cols - 1 THEN // If on the first or last col, max is a peak
RETURN max
ENDIF
IF max < A[max_index][mid - 1] // If max is less than left
RETURN FINDMAX(A, rows, cols, mid - (mid / 2))
ELSE IF max < A[max_index][mid + 1] // If max is less than right
RETURN FINDMAX(A, rows, cols, mid + (mid / 2))
ELSE IF max >= A[max_index][mid - 1] AND max >= A[max_index][mid + 1] // If max is more than left and right
RETURN max
ENDIF
STOP
FINDMAX(A, rows, mid, max)
START
max_index <-- 0
FOR i = 0 to row
IF max < A[i][j] THEN // Find global max on col mid, [i, mid]
max <-- A[i][mid]
max_index <-- i
ENDIF
ENDFOR
END
Complexity = \(T\)(m/2) + \(\Theta\)(n) cost of finding the maximum in a col
\(T\)(n) = \(T\)(n/2) + \(\Theta\)(n) = \(T\)(n/4) + 2 * \(\Theta\)(n) = \(T\)(n/16) + 3 * \(\Theta\)(n)
= log(n) \(\Theta\)(n) = \(\Theta\)(n log n)
\(\Theta\)(n log m)
\(T\)(n) = 3\(T\)(n/2) + n2
a = 3, b = 2, f(n) = n2
height = log2(n), #leaves = 3log2(n) = nlog3
Compare nlog3 and n2
Complexity = \(\Theta\)(n2)
\(T\)(n) = 9\(T\)(n/3) + n2
a = 9, b = 3, f(n) = n2
height = log3(n), #leaves = 9log3(n) = nlog3(9) = n2
Complexity = \(\Theta\)(n2 log n)
\(T\)(n) = \(T\)(n/2) + 2n
a = 1, b = 2, f(n) = 2n
height = log2(n), #leaves = n^log2(1) = 1
Compare 2n and 1
Complexity = \(\Theta\)(2n) < 2n + 2n = 2 * 2n = \(\Theta\)(2n)
\(T\)(n) = 2n + 2(n/2) + … + 2 + 1
2k > 2(k-1) + 2(k-2) + … + 2 + 1
(2k) - 1