Rakin Sir Merged
Rakin Sir Merged
6 4 3 8 1 5 2 7
1 2 3 4 5 6 7 8
6 4 3 8 5
4 6 3 8 5
Insertion Sort 6 4 3 8 5
example
Insertion-Sort(A, n)
for i = 1 to n – 1
Then move A[2]:
key = A[i] key = 3
j = i – 1
while j >= 0 and A[j] > key
A[j + 1] = A[j]
4 6 3 8 5
j = j – 1
A[j + 1] = key
4 6 6 8 5
4 4 6 8 5
3 4 6 8 5
Insertion Sort 6 4 3 8 5
example
Insertion-Sort(A, n)
for i = 1 to n – 1
Then move A[3]:
key = A[i] key = 8
j = i – 1
while j >= 0 and A[j] > key
A[j + 1] = A[j]
j = j – 1
A[j + 1] = key 3 4 6 8 5
3 4 6 8 5
Insertion Sort 6 4 3 8 5
example
Insertion-Sort(A, n)
for i = 1 to n – 1
Then move A[4]:
key = A[i] key = 5
j = i – 1
while j >= 0 and A[j] > key
A[j + 1] = A[j]
j = j – 1 3 4 6 8 5
A[j + 1] = key
3 4 6 8 8
3 4 6 6 8
3 4 5 6 8
Insertion Sort 6 4 3 8 5
example
Start by moving A[1] toward
the beginning of the list until
you find something smaller
(or can’t go any further): Then move A[3]:
6 4 3 8 5 3 4 6 8 5
4 6 3 8 5 3 4 6 8 5
Then move A[2]: Then move A[4]:
4 6 3 8 5 3 4 6 8 5
3 4 6 8 5 3 4 5 6 8
Then we are done!
Why does this work?
Proof By
Induction!
Outline of a proof by induction
Let A be a list of length n
• Base case:
• A[:1] is sorted at the end of the 0’th iteration. ✓
• Inductive Hypothesis:
• A[:i+1] is sorted at the end of the ith iteration (of the outer loop).
• Inductive step:
• For any 0 < k < n, if the inductive hypothesis holds for i=k-1, then it holds
for i=k.
• Aka, if A[:k] is sorted at step k-1, then A[:k+1] is sorted at step k
(previous slide)
• Conclusion:
• The inductive hypothesis holds for i = 0, 1, …, n-1.
• In particular, it holds for i=n-1.
• At the end of the n-1’st iteration (aka, at the end of the algorithm), A[:n] =
A is sorted.
• That’s what we wanted! ✓
Worst-case Analysis
• In this class we will use worst-case analysis:
• We assume that a “bad guy” produces a worst-case
input for our algorithm, and we measure performance
on that worst-case input.
By my count*…
• 2𝑛2 − 𝑛 − 1 variable assignments
• 2𝑛2 − 𝑛 − 1 increments/decrements
• 2𝑛2 − 4𝑛 + 1 comparisons
• … *A complete count of the operation will be insignificant from
later discussion.
In this class we will use…
• Big-Oh notation!
• Gives us a meaningful way to talk about the
running time of an algorithm, independent of
programming language, computing platform, etc.,
without having to count all the operations.
Main idea:
Focus on how the runtime scales with n (the input size).
Asymptotic Running
Number of operations
Time
1
⋅ 𝑛2 + 100 𝑂 𝑛2
10
0.063 ⋅ 𝑛2 − .5 𝑛 + 12.7 𝑂 𝑛2
• We say “𝑇 𝑛 is 𝑂 𝑔 𝑛 ” if:
for all large enough n,
𝑇 𝑛 is at most some constant multiple of 𝑔 𝑛 .
• Formally,
𝑇 𝑛 =𝑂 𝑔 𝑛
“If and only if” “For all”
⟺
∃𝑐 > 0, 𝑛0 𝑠. 𝑡. ∀𝑛 ≥ 𝑛0 ,
“There exists” 𝑇 𝑛 ≤ 𝑐 ⋅ 𝑔(𝑛)
Ω(…) means a lower bound
• We say “𝑇 𝑛 is Ω 𝑔 𝑛 ” if, for large enough n,
𝑇 𝑛 is at least as big as a constant multiple of 𝑔 𝑛 .
• Formally,
𝑇 𝑛 =Ω 𝑔 𝑛
⟺
∃𝑐 > 0 , 𝑛0 𝑠. 𝑡. ∀𝑛 ≥ 𝑛0 ,
𝑐⋅𝑔 𝑛 ≤𝑇 𝑛
Switched these!!
Θ(…) means both!
• We say “𝑇 𝑛 is Θ 𝑔(𝑛) ” iff both:
𝑇 𝑛 =𝑂 𝑔 𝑛
and
𝑇 𝑛 =Ω 𝑔 𝑛
Insertion Sort: running time
def InsertionSort(A):
for i in range(1,len(A)):
current = A[i]
j = i-1
while j >= 0 and A[j] > current: n-1 iterations
A[j+1] = A[j] of the outer
j -= 1 loop
A[j+1] = current
Can we do better?
Can we do better?
• MergeSort: a divide-and-conquer approach
Big problem
Smaller Smaller
problem problem
Recurse! Recurse!
6 4 3 8 1 5 2 7
Recursive magic! Recursive magic!
3 4 6 8 1 2 5 7
MERGE! 1 2 3 4 5 6 7 8
MergeSort Pseudocode
MERGESORT(A):
• n = length(A)
• if n ≤ 1: If A has length 1,
It is already sorted!
• return A
Sort the left half
• L = MERGESORT(A[0 : n/2])
Sort the right half
• R = MERGESORT(A[n/2 : n])
• return MERGE(L, R) Merge the two halves
What actually happens?
First, recursively break up the array all the way down to the
base cases
6 4 3 8 1 5 2 7
6 4 3 8 1 5 2 7
6 4 3 8 1 5 2 7
6 4 3 8 1 5 2 7
This array of
length 1 is
sorted!
Then, merge them all back up!
Sorted sequence!
1 2 3 4 5 6 7 8
Merge!
3 4 6 8 1 2 5 7
Merge! Merge!
4 6 3 8 1 5 2 7
Merge! Merge! Merge! Merge!
6 4 3 8 1 5 2 7
A bunch of sorted lists of length 1 (in the order of the original sequence).
Does it work?
• Yet another job for proof by induction!!!
• Try it yourself.
Assume that n is a power of 2
for convenience.
It’s fast
CLAIM:
MergeSort runs in time 𝑂 𝑛 log 𝑛
+
Time spent within the
n/2t+1 n/2t+1 two sub-problems
How much work in this sub-problem?
Let k=n/2t…
+
Time spent within the
k/2 k/2 two sub-problems
How long does it k
take to MERGE? k/2 k/2
Answer: It takes time O(k), since we just walk across the list once.
k/2 k/2
3 4 6 8 1 2 5 7
MERGE! 1 2 3 4 5 6 7 8
k
Recursion tree
Size n
n/2 n/2
n/2t n/2t n/2t n/2t n/2t n/2t There are O(k) operations
done at this node.
…
k
k/2 k/2
(Size 1)
Recursion tree
How many operations are done at this level of the
Size n tree? (Just MERGE-ing subproblems).
…
This level?
n/2t n/2t n/2t n/2t n/2t n/2t There are O(k) operations
done at this node.
…
k
k/2 k/2
(Size 1)
Work this out yourself!
Recursion tree #
Size of
each
Amount of work
Level problems problem at this level
Size n
0 1 n O(n)
n/2 n/2
1 2 n/2 O(n)
n/4 n/4 n/4 n/4 2 4 n/4 O(n)
… …
n/2t n/2t n/2t n/2t n/2t
n/2t t 2t n/2t O(n)
… …
log(n) n 1 O(n)
(Size 1)
Total runtime…
• log(n) + 1 levels
• O( n log(n) ) total!
• Divide
• Partition the array A[1:n] into two (possibly empty) subarrays
A[1:q-1] (the low side) and A [q+1:n] (the high side)
• Each element in the low side of the partition is <=A[q]
Each element in the high side is of the partition >= A[q].
• Compute the index q of the pivot as part of this partitioning
procedure.
• Conquer
• Recursively sort the subarrays A[1:q-1] and A[q+1:n]
• Combine
• Already sorted
Pseudocode of QuickSort
QUICKSORT(A, p, r)
if p < r
q = PARTITION(A, p, r)
QUICKSORT(A, p, q – 1)
QUICKSORT(A, q+1, r)
PARTITION
PARTITION(A, p, r)
x = A[r]
i = p – 1
for j = p to r – 1
if A[j] <= x
i = i + 1
exchange A[i] with A[j]
exchange A[i + 1] with A[r]
return i + 1
Example of PARTITION
i = -1
7 6 3 5 1 2 4 Pick 4 as a pivot
j = 0 j = 1j = 2
Example of PARTITION
i = -1i = 0
3 6 7 5 1 2 4 Pick 4 as a pivot
j=3j=4
Example of PARTITION
i=0i=1
3 1 7 5 6 2 4 Pick 4 as a pivot
j=5
Example of PARTITION
i=1i=2
3 1 2 5 6 7 4 Pick 4 as a pivot
Example of PARTITION
1 2 3 4 5 6 7
QuickSort Runtime Analysis
𝑇 𝑛 = The worst-case running time on a problem of
size n
• Worst-case partitioning
• Best-case partitioning
Recurrences
• An equation that describes a function in terms of
its value on other, typically smaller, arguments.
• Recursive Case
• Involves the recursive invocation of the function on different
(usually smaller) inputs
• Base Case
• Does not involve a recursive invocation
Algorithmic Recurrences
• A recurrence 𝑇 𝑛 is algorithmic if, for every
sufficiently large threshold constant 𝑛0 < 𝑛, the
following two properties hold,
Solving Recurrences
• Substitution Method
• Guess a solution
• Use mathematical induction to prove the guess
Substitution Method
𝑛
𝑇 𝑛 = 2𝑇 + Θ(𝑛)
2
• Guess
• 𝑇 𝑛 = 𝑂(𝑛𝑙𝑔𝑛)
• We need to prove,
• 𝑇 𝑛 ≤ 𝑐𝑛𝑙𝑔𝑛 for all, 𝑛0 ≤ 𝑛
• For specific choice of 𝑐 > 0 and n0 > 0
Substitution Method
• Inductive Hypothesis
• 𝑇 𝑛′ ≤ 𝑐𝑛′𝑙𝑔𝑛′ for all n0 < 𝑛′ < 𝑛 and 2n0 ≤ 𝑛
𝑛
𝑇 𝑛 = 2𝑇 +Θ 𝑛
𝑛 2 𝑛
𝑇 𝑛 ≤ 2c lg + Θ(𝑛)
2𝑛 2
𝑇 𝑛 ≤ cnlg + Θ 𝑛
2
𝑇 𝑛 ≤ cnlg 𝑛 − 𝑐𝑛𝑙𝑔2 + Θ(𝑛)
𝑇 𝑛 ≤ cnlg 𝑛 − 𝑐𝑛 + Θ(𝑛)
• Assuming
• 𝑛0 = 2
• 𝑐 = max(𝑇 2 , 𝑇(3))
• We get 𝑇 𝑛 ≤ 𝑐𝑛𝑙𝑔𝑛
Solving Recurrences
• Substitution Method
• Guess a solution
• Use mathematical induction to prove the guess
• Example
Master Theorem
• Let’s consider the following recurrence relation,
𝑇 𝑛 = Θ(𝑛2 )
Master Theorem
2𝑛
𝑇 𝑛 = 𝑇 +1
3
2
• 𝑎 = 1, 𝑏 =
3
• 𝑓 𝑛 = 1 = 𝑂 𝑛0 = 𝑂 𝑛𝑐
• log 𝑏 𝑎 = 𝑙𝑜𝑔2/3 1 = 0 = 𝑐
𝑇 𝑛 = Θ 𝑛0 𝑙𝑔𝑛 = Θ(𝑙𝑔𝑛)
Master Theorem
𝑛
𝑇 𝑛 = 3𝑇 + 𝑛 lg 𝑛
4
• 𝑎 = 3, 𝑏 = 4
• 𝑓 𝑛 = 𝑛 lg 𝑛 > 𝑛 = Ω 𝑛1 = Ω(𝑛𝑐 )
• log 𝑏 𝑎 = 𝑙𝑜𝑔4 3 < 𝑐
• Can we apply case 3?
𝑛 3𝑛 3 3
• 𝑎𝑓 = lg ≤ 𝑛 lg 𝑛 ≤ 𝐶𝑓 𝑛
𝑏 4 4 4
𝑇 𝑛 = Θ(𝑛 lg 𝑛)
Master Theorem
𝑛
𝑇 𝑛 = 2𝑇 + 𝑛 lg 𝑛
2
𝑇 𝑛 = Θ(𝑛 lg 2 𝑛)
MergeSort Runtime Analysis
(Revisit)
𝑛
𝑇 𝑛 = 2𝑇 + Θ(𝑛)
2
𝑇 𝑛 = Θ(𝑛 lg 𝑛)
QuickSort Runtime Analysis
(Revisit)
• Best-case partitioning
• Worst-case partitioning
• Conquer
Multiplying Square Matrices
Θ(1)
𝑛
8𝑇( )
2
Multiplying Square Matrices
• Running Time
𝑛
𝑇 𝑛 =8𝑇 +Θ 1
2
𝑇 𝑛 = Θ 𝑛3
Can we do better?
Strassen’s Algorithm
• Intuitions,
• Addition is faster than multiplications.
• Θ 𝑛2 𝑣𝑠 Θ(𝑛3 )
𝑇 𝑛 = Θ 𝑛log2 7 = Θ(𝑛2.80755… )
Reference
• Introduction to Algorithms, CLRS, 4th edition.
• Chapter 2 (Getting Started)
• Section 2.1, 2.3
• Chapter 4 (Divide-and-Conquer)
• Sections 4.1 – 4.5
• Chapter 7 (Quick Sort)
• Sections 7.1 and 7.2
• Additional Resource:
• mastertheorem.pdf
Dynamic Programming
Fibonacchi Numbers
𝑓𝑖𝑏 𝑛 = 𝑓𝑖𝑏 𝑛 − 1 + 𝑓𝑖𝑏 𝑛 − 2
• Write a program to find the nth fibonacchi number.
def Fibonacci(n):
if n == 0: return 0
if n == 1: return 1
return Fibonacci(n-1) + Fibonacci(n-2)
• Runtime analysis
𝑇 𝑛 = 𝑇 𝑛 − 1 + 𝑇 𝑛 − 2 + 𝑂(1)
𝑇 𝑛 = Ω(2𝑛/2 )
What’s going on? That’s a lot of
repeated
Consider Fib(8) computation!
8
6 7
4 5 5 6
2 3 3 4 3 4 4 5
0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4
0 1
1 2 1 2 ……
0 1 0 1 1 2 0 1 0 1 0 1 1 2
0 1 0 1 0 1 0 1
Fibonacchi Numbers
• How to avoid repeated computations?
• Store already computed results
def Fibonacci(n):
local fib[n+1]
fib[0] = 0
fib[1] = 1
for i -> 2 to n:
fib[i] = fib[i-1] + fib[i-2]
return fib[n]
What did we do?
Dynamic Programming!!!
Dynamic Programming
• It is an algorithm design paradigm
• like divide-and-conquer is an algorithm design paradigm.
• Usually, it is for solving optimization problems
• E.g., shortest path, minimum/maximum profit, longest sequences
• (Fibonacci numbers aren’t an optimization problem, but they are a good
example of DP anyway…)
Elements of dynamic programming
1. Optimal Sub-structure Property
• Big problems break up into sub-problems.
• The solution to a problem can be expressed in terms of solutions to
smaller sub-problems.
• Fibonacci:
𝑓𝑖𝑏 𝑛 = 𝑓𝑖𝑏 𝑛 − 1 + 𝑓𝑖𝑏(𝑛 − 2)
Elements of dynamic programming
2. Overlapping Sub-Problem Property
• The sub-problems overlap.
• Fibonacci:
• Both fib[i+1] and fib[i+2] directly use fib[i].
• And lots of different fib[i+x] indirectly use fib[i].
• This means that we can save time by solving a sub-problem just
once and storing the answer.
Elements of dynamic programming
1. Optimal substructure.
• Optimal solutions to sub-problems can be used to find the optimal
solution of the original problem.
2. Overlapping subproblems.
• The subproblems show up again and again
6 7
4 5 5 6
2 3 3 4 3 4 4 5
0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4
1 2 etc
0 1 1 2
0 1 0 1 1 2 0 1 0 1 0 1 1 2
0 1 0 1 0 1 0 1
13
Top-down Approach
Nodes Pruned
8
6 7
4 5 5 6
2 3 3 4 3 4 4 5
0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4
1 2 etc
0 1 1 2
0 1 0 1 1 2 0 1 0 1 0 1 1 2
0 1 0 1 0 1 0 1
14
Bottom-up Approach 8
7
• Solve the small problems first
• fill in fib[0],fib[1]
6
• Then bigger problems
• fill in fib[2] 5
•…
4
• Then bigger problems
• fill in fib[n-1] 3
• fill in fib[n] 1
0
Rod-cutting Problem
• Given
• A rod of length n
• A table of prices 𝑝𝑖 for 𝑖 = 1, 2, … , 𝑛,
• Determine the maximum revenue 𝑟𝑛 obtainable by cutting up the
rod and selling all the pieces.
• For example,
Rod-cutting Problem
Rod-cutting Problem
• An optimal solution involving k pieces,
• Each piece has length 𝑖1 , 𝑖2 , 𝑖3, … , 𝑖𝑘
• 𝑛 = 𝑖1 + 𝑖2 + 𝑖3 + ⋯ + 𝑖𝑘
• The optimal revenue, 𝑟𝑛 = 𝑝𝑖1 + 𝑝𝑖2 + ⋯ + 𝑝𝑖𝑘
Rod-cutting Problem
• Once an initial cut is made,
• The two resulting smaller pieces will be cut independently
• Smaller instance of the rod-cutting problem
• Optimal Sub-structure Property
• Different pieces can be cut into same length pieces (on not)
• Overlapping Sub-structure Property
Rod-cutting Problem
• Assuming an initial cut is made,
• 𝑟𝑛 = max(𝑝𝑛 , 𝑟1 + 𝑟𝑛−1 , 𝑟2 + 𝑟𝑛−2 , … 𝑟𝑛−1 + 𝑟1 )
Runtime O(2𝑛−1 )
Rod-cutting Problem (Top-down Approach)
Rod-cutting Problem (Bottom-up Approach)
Runtime 𝑂(𝑛2 )
Rod-cutting Problem (Bottom-up Approach)
• Reconstruct the choices that led to the optimal solution
• Example,
• 𝐴1 , 𝐴2 , 𝐴3 with dimensions 10 * 100, 100 * 5, 5 * 50
• ((𝐴1 , 𝐴2 ), 𝐴3 ) perfoms a total of 7500 scalar multiplication
• (𝐴1 , (𝐴2 , 𝐴3 )) perfoms a total of 75000 scalar multiplication
Matrix Chain Multiplication
• Parenthesizing resolves ambiguity in multiplication order
• Fully parenthesized chain of matrices
• Either a single matrix
• Or the product of two fully parenthesized matrix products, surrounded by
parentheses
• Example,
• < 𝐴1 , 𝐴2 , 𝐴3 , 𝐴4 > can be parenthesized in 5 distinct ways.
• (𝐴1 , (𝐴2 , (𝐴3 , 𝐴4 ))), (𝐴1 , ((𝐴2 , 𝐴3 ), 𝐴4 )), ((𝐴1 , 𝐴2 ), (𝐴3 , 𝐴4 )),
((𝐴1 , (𝐴2 , 𝐴3 )), 𝐴4 ), (((𝐴1 , 𝐴2 ), 𝐴3 ), 𝐴4 )
Matrix Chain Multiplication
• Given a chain of n matrices, 𝐴1, 𝐴2 , … , 𝐴𝑛
• Matrix 𝐴𝑖 has dimensions 𝑝𝑖−1 ∗ 𝑝𝑖
• Goal: Fully parenthesize the product to minimize the number of scalar
multiplications
• Let, the first split occurs between kth and (k+1)st matrices
𝑚 𝑖, 𝑗 = 𝑚 𝑖, 𝑘 + 𝑚 𝑘 + 1, 𝑗 + 𝑝𝑖−1 ∗ 𝑝𝑘 ∗ 𝑝𝑗
• We need to consider all such splits, i.e., all values of k
Matrix Chain Multiplication
• The optimal parenthesization
• Split the product 𝐴𝑖:𝑗 between 𝐴𝑘 and 𝐴𝑘+1 for some value of 𝑖 ≤ 𝑘 < 𝑗
𝑚 𝑖, 𝑗 = 𝑚 𝑖, 𝑘 + 𝑚 𝑘, +1 𝑗 + 𝑝𝑖−1 ∗ 𝑝𝑘 ∗ 𝑝𝑗
• We need to consider all such splits, i.e., all values of k
5 2500 1000 0
6 3500 5000 0
Matrix Chain Multiplication
𝐴1 𝐴2 𝐴3 𝐴4 𝐴5 𝐴6
30 * 35 35 * 15 15 * 5 5 *10 10 * 20 20 * 25
j\i 1 2 3 4 5 6
1 0
2. Overlapping subproblems.
• The subproblems show up again and again
Optimal Substructure Property
• Solution to sub-problems are included in the optimal solution
• Rod-cutting
• Solution to smaller pieces are also part of the solution to the entire rod
• S1 = ACCGGTCGAGTGCGCGGAAGCCGGCCGAA
• S2 = GTCGTTCGGAATGCCGTTGCTCTGTAAA
Longest Common Subsequence
• A strand of DNA consists of a string of molecules called bases
• Adenine, Cytosine, Guanine, and Thymine
• ACGT
𝑐𝑜𝑠𝑡 𝑀
= 𝑐𝑜𝑠𝑡𝑚𝑖𝑠𝑚𝑎𝑡𝑐ℎ + 𝑐𝑜𝑠𝑡𝑔𝑎𝑝 + 𝑐𝑜𝑠𝑡𝑔𝑎𝑝
𝑥𝑖 ,𝑦𝑗 ∈𝑀 𝑥𝑖 𝑦𝑗
𝑢𝑛𝑚𝑎𝑡𝑐ℎ𝑒𝑑 𝑢𝑛𝑚𝑎𝑡𝑐ℎ𝑒𝑑
Sequence Alignment
• Given two sequences
• 𝑥1 𝑥2 … 𝑥𝑛 and 𝑦1𝑦2 … 𝑦𝑛
Big problem
sub-problem sub-problem
Big problem
• Goal:
• Schedule maximum-size subset of mutually compatible activities
Activity Selection Problem
• Assumption: The activities are sorted by their finish times
𝑓1 ≤ 𝑓2 ≤ 𝑓3 ≤ … ≤ 𝑓𝑛
• DP??
Activity Selection Problem
• Let’s make a greedy choice
• Choose the activity that leaves the resource available for as many
other activities as possible
• Make the choice that looks best in the current problem, without
considering results from subproblems.
Elements of Greedy Strategy
Big problem
sub-problem
sub-sub-
problem
Knapsack Problems
• 0-1 Knapsack Problem
• Choice at each step
• The choice usually depends on the solutions to subproblems.
• Goal: Find out the shortest path weight from the source to a given
node / other nodes.
Single Source Shortest Path
• Subpaths of shortest paths are shortest paths
• Proof:
• Decompose the shortest path into smaller sub-paths
p = 𝑣1 ∼ 𝑣𝑖 ∼ 𝑣𝑗 ∼ 𝑣𝑘
• The subpaths are 𝑝0𝑖 , 𝑝𝑖𝑗 , 𝑝𝑗𝑘
• Assume there exists 𝑝𝑖𝑗 ′ such that 𝑤(𝑝𝑖𝑗 ′) < 𝑤(𝑝𝑖𝑗 )
• Then replacing 𝑝𝑖𝑗 with 𝑝𝑖𝑗 ′ gives a shorter path
• Cycle
• Shortest path cannot contain cycle
• Just dropping the cycle gives a lower cost path
Dijkstra’s Algorithm
• Given
• A weighted and directed graph, 𝐺 = (𝑉, 𝐸)
• A source, 𝑠
• Non-negative weights on edges, 𝑤 𝑢, 𝑣 ≥ 0
• Goal: Find out the shortest path weight from the source to a given
node / other nodes.
Dijkstra’s Algorithm
• A path weight of a, 𝑝 = < 𝑣1 , 𝑣2 , … , 𝑣𝑘 >, is
• Inductive Hypothesis:
• At the start of each iteration, 𝑣 = 𝛿(𝑠, 𝑣) for all 𝑣 ∈ 𝑆
Dijkstra’s Algorithm
• Correctness of Dijkstra’s Algorithm
• At some iteration, v is extracted from the priority queue
• y first node not in S on the shortest path P to u
• x predecessor of y on P
𝑦. 𝑑 ≥ 𝑣. 𝑑
𝛿 𝑠, 𝑥 + 𝑤 𝑥, 𝑦 ≥ 𝛿 𝑠, 𝑢 + 𝑤(𝑢, 𝑣)
𝑤 𝑃 ≥ 𝑤(𝑠 ∼ 𝑣)
Minimum Spanning Tree
• A spanning tree is a tree that connects all of the vertices.
• The cost of a spanning tree is the sum of the weights on the edges.
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
Minimum Spanning Tree
• A spanning tree is a tree that connects all of the vertices.
• The cost of a spanning tree is the sum of the weights on the edges.
8 7
B C D
It has cost 67
4 9
2
11 4
A I 14 E
7 6
8 10
A tree is a
1 2 connected graph
H G F
with no cycles!
Minimum Spanning Tree
• A minimum spanning tree is a tree with minimum cost that
connects all of the vertices.
8 7
B C D
It has cost 37
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
Some Definitions
• An (𝑆, 𝑉 − 𝑆) partition is a cut of 𝑉 of an undirected graph 𝐺 =
(𝑉, 𝐸)
8 7
Root Node B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
44
Prim’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
45
Prim’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
46
Prim’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
47
Prim’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
48
Prim’s Algorithm
Prim’s Algorithm
• Proof of correctness?
• Later
Minimum Spanning Tree
• Greedy Strategy 2 (Kruskal’s Algorithm):
• Start with each node as a separate tree
• Consider the edges in ascending order of their weights
• Include the minimum weight edge between two disjoint trees to connect
them into a single tree
• Discard the edge if it creates a cycle
• Terminate when all the nodes are included
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
52
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
53
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
54
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
55
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
56
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
57
Kruskal’s Algorithm
Causing a cycle
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
58
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
59
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
60
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
61
Kruskal’s Algorithm
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
62
Kruskal’s Algorithm
Kruskal’s Algorithm
Proof of Correctness?
• Later
Cut Property
• Assume that all edge costs are distinct.
• Let S be any subset of nodes that is neither empty nor equal to all
of V, and let edge e =(v, w) be the minimum cost edge with one end
in S and the other in V −S.
• Prim’s Algorithm
• Apply cut property
Reference
• Greedy Algorithms
• CLRS 4th ed. Sections 15.1, 15.2
• Dijkstra’s Algorithm
• CLRS 4th ed. Sections 22 (intro), 22.3
8 7
B C D
4 9
2
11 4
A I 14 E
7 6
8 10
1 2
H G F
2
Union-Find
• Make-Set(x)
• Creates a new set whose only
member is x
• O(1) 8 7
B C D
4 9
• MakeUnionFind(S) 2
11 4
• MakeSet(x) with each v \in V A I 14 E
8 7 6
10
H
1 G 2 F
3
Union-Find
• Find(x)
• Return a pointer to the
representative/name of the set
containing x 8 7
B C D
• Find(C) = Red/ Pointer to C or I
4 9
2
11 4
A I 14 E
8 7 6
10
H
1 G 2 F
4
Union-Find
• Find(x)
• Return a pointer to the
representative/name of the set
containing x 8 7
B C D
• Find(C) = Red/ Pointer to C or I
4 9
2
11 4
• Union(x, y) A I 14 E
• Unites two disjoint, dynamic
sets that contain x and y, say 𝑆𝑥 8 7 6
10
and 𝑆𝑦 1 2
H G F
5
Union-Find
• Maintain an array Component
• Contains the name of the set
currently containing each
element. 8 7
B C D
4 9
• Name of the set 2
11 4
• Pointer to a representative node A I 14 E
• Name of the representative node
8 7 6
10
H
1 G 2 F
6
Union-Find
Components
1 1 3 4 5 6 6 6 3
• Maintain an array Component 1 2 3 4 5 6 7 8 9
• Contains the name of the set
currently containing each
element. 8 7
2 3 4
4 9
• Name of the set 2
11 4
• Pointer to a representative node 1 9 14 5
• Name of the representative node
• Index of the representative node 8 7 6
10
8
1 7 2 6
7
Union-Find
Components
1 2 3 4 5 6 7 8 9
• Initially set 𝐶𝑜𝑚𝑝𝑜𝑛𝑒𝑛𝑡[𝑠] = 𝑠 1 2 3 4 5 6 7 8 9
for all s.
8 7
2 3 4
4 9
2
11 4
1 9 14 5
8 7 6
10
8
1 7 2 6
8
Union-Find
Components
1 1 3 4 5 6 7 8 9
• Initially set 𝐶𝑜𝑚𝑝𝑜𝑛𝑒𝑛𝑡[𝑠] = 𝑠 1 2 3 4 5 6 7 8 9
for all s.
8 7
2 3 4
• Union(x, y) merges two
4 9
disjoint sets together 2
• Update the values of 11 4
𝐶𝑜𝑚𝑝𝑜𝑛𝑒𝑛𝑡[𝑠] for all elements 1 9 14 5
in sets A and/or B
8 7 6
10
8
1 7 2 6
9
Union-Find
Components
1 1 3 4 5 6 6 6 3
• Initially set Component[s]=s 1 2 3 4 5 6 7 8 9
for all s
8 7
2 3 4
• Union(x, y) merges two disjoint
4 9
sets together 2
• Update the values of 11 4
Component[s] for all elements in 1 9 14 5
sets A and/or B
8 7 6
10
8
1 7 2 6
10
Union-Find
Components
1 1 3 4 5 3 3 3 3
• Initially set Component[s]=s 1 2 3 4 5 6 7 8 9
for all s
8 7
2 3 4
• Union(x, y) merges two disjoint
4 9
sets together 2
• Update the values of 11 4
Component[s] for all elements in 1 9 14 5
sets A and B
8 7 6
• Scan all the components 10
1 2
• Can take 𝑂(𝑛) 8 7 6
11
Union-Find
Components
1 1 3 4 5 3 3 3 3
• Find(x) 1 2 3 4 5 6 7 8 9
• Return Components[x]
• Takes 𝑂(1)
8 7
2 3 4
4 9
2
11 4
1 9 14 5
8 7 6
10
8
1 7 2 6
12
Union-Find
Components
1 1 3 4 5 3 3 3 3
• Optimizations to improve the 1 2 3 4 5 6 7 8 9
Union(x, y)
• Maintain the list of 8 7
elements in each 2 3 4
component 4 9
2
• Only update the elements in 11 4
the smaller set; Keep the 1 9 14 5
name of the larger set
8 7 6
10
1 2
• Still 𝑂(𝑛) 8 7 6
13
Union-Find
• Any sequence of k Union operations takes at most 𝑂(𝑘 log 𝑘) time
• Touches at most 2k elements of S
• A node v’s set grows after each Union operation
• Either Component[v] remains unchanged, or it is updated
• If updated the size of v’s set doubles
• There can be at most log(2𝑘) updates to Component[v]
• For 2k node, there can be at most 𝑂 𝑘𝑙𝑜𝑔𝑘 updates.
A Better Union-Find Parents
1 2 3 4 5 6 7 8 9
• Each node v will point to the 1 2 3 4 5 6 7 8 9
representative node of its set.
8 7
2 3 4
• MakeUnionFind(S) initializes a
4 9
record for each element v with 2
a pointer that points to itself 11 4
1 9 14 5
• To indicate that v is in its own
set. 7 6
8 10
8
1 7 2 6
A Better Union-Find
• Consider a Union(x, y)
• Set either x or y be the name of the combined set (preferably from the
larger set)
• Assume we select y as the name.
• Simply update x’s pointer to point to y.
• We do not update the pointers at the other nodes in x’s set.
A Better Union-Find Parents
1 2 3 4 5 6 8 8 9
• Consider a Union(x, y) 1 2 3 4 5 6 7 8 9
• Set either x or y be the name of
the combined set
• Assume we select y as the 8 7
2 3 4
name.
4 9
• Simply update x’s pointer to 2
point to y. 11 4
1 9 14 5
• We do not update the pointers at
the other nodes in x’s set. 7 6
8 10
8
1 7 2 6
A Better Union-Find Parents
1 2 3 4 5 6 8 8 3
• Consider a Union(x, y) 1 2 3 4 5 6 7 8 9
• The idea is to have either x or y
be the name of the combined set
• Assume we select y as the 8 7
2 3 4
name.
4 9
• Simply update x’s pointer to 2
point to y. 11 4
1 9 14 5
• We do not update the pointers at
the other nodes in x’s set. 7 6
8 10
8
1 7 2 6
A Better Union-Find Parents
1 2 3 4 5 7 8 8 3
• Consider a Union(x, y) 1 2 3 4 5 6 7 8 9
• The idea is to have either x or y
be the name of the combined set
• Assume we select y as the 8 7
2 3 4
name.
4 9
• Simply update x’s pointer to 2
point to y. 11 4
1 9 14 5
• We do not update the pointers at
the other nodes in x’s set. 7 6
8 10
8
1 7 2 6
A Better Union-Find Parents
1 1 3 4 5 7 8 8 3
• Consider a Union(x, y) 1 2 3 4 5 6 7 8 9
• The idea is to have either x or y
be the name of the combined set
• Assume we select y as the 8 7
2 3 4
name.
4 9
• Simply update x’s pointer to 2
point to y. 11 4
1 9 14 5
• We do not update the pointers at
the other nodes in x’s set. 7 6
8 10
8
1 7 2 6
A Better Union-Find Parents
1 1 6 4 5 7 8 8 3
• Consider a Union(x, y) 1 2 3 4 5 6 7 8 9
• The idea is to have either x or y
be the name of the combined set
• Assume we select y as the 8 7
2 3 4
name.
4 9
• Simply update x’s pointer to 2
point to y. 11 4
1 9 14 5
• We do not update the pointers at
the other nodes in x’s set. 7 6
8 10
8
1 7 2 6
A Better Union-Find
Parents
1 1 6 4 5 7 8 8 3
• Union(x, y) 1 2 3 4 5 6 7 8 9
• Takes O(1)
8 7
2 3 4
• Find(x)
• Cannot simply return Parents[s] 4 9
2
• Traverse through the pointers to 11 4
the top 1 9 14 5
• No longer O(1)
8 7 6
10
8
1 7 2 6
A Better Union-Find
• Find operation takes O(log n) time
• Every time the name of the set containing node v changes, the size of this
set at least doubles.
• There can be at most n nodes in a set
• There can be at most name log 𝑛 changes
• Find operation has 𝑂(log 𝑛) complexity
A Better Union-Find
def MakeUnionFind(n) def find(x):
for i = 1 to n if parent[x] == x
parent[i] = i return parent[x]
else
def Union(x, y): return find(parent[x])
# Assuming x and y are
# from two disjoint sets.
if x’s set is larger
parent[y] = x
else
parent[x] = y
A Better Union-Find with Path Compression
def MakeUnionFind(n) def find(x):
for i = 1 to n if parent[x] == x
parent[i] = i return x
else
def Union(x, y): parent[x] = find(parent[x])
# Assuming x and y are return parent[x]
# from two disjoint sets.
if x’s set is larger
parent[y] = x
else
parent[x] = y
Reference
• Union-Find
• KT Section 4.6
Sorting In Linear Time
Sorting
• Sort n integers in Ο(𝑛 𝑙𝑜𝑔𝑛)
• Merge Sort, Heap Sort in the worst case
• Quick Sort on average
• Runs in Θ 𝑛 + 𝑘
C
2 0 2 3 0 1
Counting Sort
A
2 5 3 0 2 3 0 3
C (old)
2 0 2 3 0 1
C (current state)
2 2 4 7 7 8
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
2 2 4 7 7 8
B
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
2 2 4 7 7 8
B
3
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
2 2 4 6 7 8
B
3
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
2 2 4 6 7 8
B
0 3
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
1 2 4 6 7 8
B
0 3
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
1 2 4 6 7 8
B
0 3 3
Counting Sort
A
2 5 3 0 2 3 0 3
C (current state)
0 2 2 4 7 7
B
0 0 2 2 3 3 3 5
Counting Sort
• Counting sort is stable
• Elements with the same value appear in the output array in the same
order as they do in the input array.
Radix Sort
• We can sort the elements
• By most significant (leftmost) digit
• Then sort the resulting bins recursively
• Finally combine the bins in order.
• Generates many intermediate bins to track
Radix Sort
• Let’s start by the least significant bit
• For example,
Radix Sort