ALgorithm Combined
ALgorithm Combined
By
Dr. Swagata Mandal
Department of Electronics & Comm.
Jalpaiguri Government Engineering College
What is course about?
The theoretical study of design and
analysis of computer algorithms
Basic goals for an algorithm:
• always correct
• always terminates
• This class: performance
Performance often draws the line
between
what is possible and what is impossible.
What is an algorithm?
Example:
Input: 8 2 4 9 3 6
Output: 2 3 4 6 8 9
Insertion sort
INSERTION-SORT (A, n) ⊳ A[1 . . n]
for j ← 2 to n
do key ← A[ j]
i←j–1
“pseudocode” while i > 0 and A[i] > key
do A[i+1] ← A[i]
i←i–1
A[i+1] = key
1 i j n
A:
key
sorted
Example of insertion sort
8 2 4 9 3 6
Example of insertion sort
8 2 4 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
2 4 8 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
2 4 8 9 3 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
2 4 8 9 3 6
2 3 4 8 9 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
2 4 8 9 3 6
2 3 4 8 9 6
Example of insertion sort
8 2 4 9 3 6
2 8 4 9 3 6
2 4 8 9 3 6
2 4 8 9 3 6
2 3 4 8 9 6
2 3 4 6 8 9 done
Running time
BIG IDEAS:
• Ignore machine dependent constants,
otherwise impossible to verify and to compare algorithms
“Asymptotic Analysis”
Q-notation
DEF:
Q(g(n)) = { f (n) : there exist positive constants c1, c2, and
n0 such that 0 c1 g(n) f (n) c2 g(n)
for all n n0 }
Basic manipulations:
• Drop low-order terms; ignore leading constants.
• Example: 3n3 + 90n2 – 5n + 6046 = Q(n3)
Big O notation
• O notation is used to describe asymptotic upper bound.
• Mathematically, if f(n) describe running time of an algorithm;
f(n) is O(g(n)) if there exist positive constant C and no such
that
• 0 <=f(n) <= c g(n) for all n>=n0 n = used to give upper
bound an a function.
If a function is O(n), it is automatically O(n-square) as well !
Ω- notation
• Just like O notation provide an asymptotic upper bound, Ω
notation provides asymptotic lower bound.
• Let f(n) define running time of an algorithm; f(n) is said to
be Ω(g (n)) if there exists positive constant C and (n0) such
that
O<= C g(n) <= f(n) for all n>=n0
n= used to given lower bound on a function
Asymptotic performance
When n gets large enough, a Q(n2) algorithm
always beats a Q(n3) algorithm.
.
• Asymptotic analysis is a
useful tool to help to
structure our thinking
toward better algorithm
• We shouldn’t ignore
T(n) asymptotically slower
algorithms, however.
• Real-world design
situations often call for a
n n0
careful balancing
Insertion sort analysis
Worst case: Input reverse sorted.
n
T ( n) Q ( j ) Q n 2 [arithmetic series]
j 2
Average case: All permutations equally likely.
n
T ( n) Q( j / 2) Qn 2
j 2
Is insertion sort a fast sorting algorithm?
• Moderately so, for small n.
• Not at all, for large n.
Example 2: Integer Multiplication
MERGE-SORT A[1 . . n]
1. If n = 1, done.
2. Recursively sort A[ 1 . . n/2 ]
and A[ n/2+1 . . n ] .
3. “Merge” the 2 sorted lists.
1
Merging two sorted arrays
20 12 20 12
13 11 13 11
7 9 7 9
2 1 2
1
Merging two sorted arrays
20 12 20 12
13 11 13 11
7 9 7 9
2 1 2
1 2
Merging two sorted arrays
20 12 20 12 20 12
13 11 13 11 13 11
7 9 7 9 7 9
2 1 2
1 2
Merging two sorted arrays
20 12 20 12 20 12
13 11 13 11 13 11
7 9 7 9 7 9
2 1 2
1 2 7
Merging two sorted arrays
20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11
7 9 7 9 7 9 9
2 1 2
1 2 7
Merging two sorted arrays
20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11
7 9 7 9 7 9 9
2 1 2
1 2 7 9
Merging two sorted arrays
20 12 20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11 13 11
7 9 7 9 7 9 9
2 1 2
1 2 7 9
Merging two sorted arrays
20 12 20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11 13 11
7 9 7 9 7 9 9
2 1 2
1 2 7 9 11
L1.40
Merging two sorted arrays
20 12 20 12 20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11 13 11 13
7 9 7 9 7 9 9
2 1 2
1 2 7 9 11
Merging two sorted arrays
20 12 20 12 20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11 13 11 13
7 9 7 9 7 9 9
2 1 2
1 2 7 9 11 12
Merging two sorted arrays
20 12 20 12 20 12 20 12 20 12 20 12
13 11 13 11 13 11 13 11 13 11 13
7 9 7 9 7 9 9
2 1 2
1 2 7 9 11 12
Q(1)
Recursion tree
Solve T(n) = 2T(n/2) + cn, where c > 0 is constant.
cn
cn/2 cn/2
h = lg n cn/4 cn/4 cn/4 cn/4
Q(1)
Recursion tree
Solve T(n) = 2T(n/2) + cn, where c > 0 is constant.
cn cn
cn/2 cn/2
h = lg n cn/4 cn/4 cn/4 cn/4
Q(1)
Recursion tree
Solve T(n) = 2T(n/2) + cn, where c > 0 is constant.
cn cn
cn/2 cn/2 cn
h = lg n cn/4 cn/4 cn/4 cn/4
Q(1)
Recursion tree
Solve T(n) = 2T(n/2) + cn, where c > 0 is constant.
cn cn
cn/2 cn/2 cn
h = lg n cn/4 cn/4 cn/4 cn/4 cn
…
Q(1)
Recursion tree
Solve T(n) = 2T(n/2) + cn, where c > 0 is constant.
cn cn
cn/2 cn/2 cn
h = lg n cn/4 cn/4 cn/4 cn/4 cn
…
Q(1) #leaves = n Q(n)
Recursion tree
Solve T(n) = 2T(n/2) + cn, where c > 0 is constant.
cn cn
cn/2 cn/2 cn
h = lg n cn/4 cn/4 cn/4 cn/4 cn
…
Q(1) #leaves = n Q(n)
TotalQ(n lg n)
Conclusions
Q(1)
Example of recursion tree
Solve T(n) = T(n/4) + T(n/2) + n2:
n2 n2
(n/4)2 (n/2)2
Q(1)
Example of recursion tree
Solve T(n) = T(n/4) + T(n/2) + n2:
n2 n2
5 n2
(n/4)2 (n/2)2
16
(n/16)2 (n/8)2 (n/8)2 (n/4)2
Q(1)
Example of recursion tree
Solve T(n) = T(n/4) + T(n/2) + n2:
n2 n2
5 n2
(n/4)2 (n/2)2
16
25 n 2
(n/16)2 (n/8)2 (n/8)2 (n/4)2
256
…
Q(1)
Example of recursion tree
Solve T(n) = T(n/4) + T(n/2) + n2:
n2 n2
5 n2
(n/4)2 (n/2)2
16
25 n 2
(n/16)2 (n/8)2 (n/8)2 (n/4)2
256
…
Q(1) Total = n2
+
1 + 16 + 16
5 5 2 5 3
16
+
= Q(n2) geometric series
Divide and Conquer Paradigm
5 2 4 6 1 3 2 6
5 2 4 6 1 3 2 6
5 2 4 6 1 3 2
2 5 4 6 1 3 2 6
2 4 5 6 1 2 3 6
1 2 2 3 4 5 6 6
Recurrence Relation for Mergesort
…
#leaves = ah
= alogbn nlogbaT (1)
T (1)
= nlogba
Three common cases
Compare f (n) with nlogba:
1. f (n) = O(nlogba – ) for some constant > 0.
• f (n) grows polynomially slower than nlogba
(by an n factor).
Solution: T(n) = Q(nlogba) .
Idea of master theorem
Recursion tree:
f (n) f (n)
a
f (n/b) f (n/b) … f (n/b) a f (n/b)
h = logbn a
f (n/b2) f (n/b2) … f (n/b2) a2 f (n/b2)
…
CASE 1: The weight increases
geometrically from the root to the nlogbaT (1)
T (1) leaves. The leaves hold a constant
fraction of the total weight. Q(nlogba)
Three common cases
Compare f (n) with nlogba:
…
CASE 2: (k = 0) The weight
nlogbaT (1)
T (1) is approximately the same on
each of the logbn levels.
Q(nlogbalg n)
Three common cases (cont.)
Compare f (n) with nlogba:
3. f (n) = W(nlogba + ) for some constant > 0.
• f (n) grows polynomially faster than nlogba (by
an n factor),
and f (n) satisfies the regularity condition that
a f (n/b) c f (n) for some constant c < 1.
Solution: T(n) = Q( f (n) ) .
Idea of master theorem
Recursion tree:
f (n) f (n)
a
f (n/b) f (n/b) … f (n/b) a f (n/b)
h = logbn a
f (n/b2) f (n/b2) … f (n/b2) a2 f (n/b2)
…
CASE 3: The weight decreases
geometrically from the root to the nlogbaT (1)
T (1) leaves. The root holds a constant
fraction of the total weight. Q( f (n))
Examples
N
Ci , j = ai ,k bk , j
k =1
Time analysis N N N
Thus T ( N ) = c = cN 3 = O ( N 3 )
i =1 j =1 k =1
Strassens’s Matrix Multiplication
• Strassen showed that 2x2 matrix multiplication can be accomplished
in 7 multiplication and 18 additions or subtractions. .(2log27 =22.807)
• This reduce can be done by Divide and Conquer Approach.
• Divide-and conquer is a general algorithm design
paradigm:
• Divide: divide the input data S in two or more disjoint
subsets S1, S2, …
• Recur: solve the subproblems recursively
• Conquer: combine the solutions for S1, S2, …, into a
solution for S
• The base case for the recursion are subproblems of
constant size
• Analysis can be done using recurrence equations
Divide and Conquer Matrix Multiply
A B = R
A0 A1 B0 B1 A0B0+A1B2 A0B1+A1B3
=
A2 A3 B2 B3 A2B0+A3B2 A2B1+A3B3
a0 b0 = a0 b0
P1 = (A11+ A22)(B11+B22)
P2 = (A21 + A22) * B11 C11 = P1 + P4 - P5 + P7
P3 = A11 * (B12 - B22) C12 = P3 + P5
P4 = A22 * (B21 - B11) C21 = P2 + P4
P5 = (A11 + A12) * B22 C22 = P1 + P3 - P2 + P6
P6 = (A21 - A11) * (B11 + B12)
P7 = (A12 - A22) * (B21 + B22)
Strassens’s Matrix Multiplication
C11 = P1 + P4 - P5 + P7
= (A11+ A22)(B11+B22) + A22 * (B21 - B11) - (A11 + A12) * B22+
(A12 - A22) * (B21 + B22)
= A11 B11 + A11 B22 + A22 B11 + A22 B22 + A22 B21 – A22 B11 -
A11 B22 -A12 B22 + A12 B21 + A12 B22 – A22 B21 – A22 B22
= A11 B11 + A12 B21
Strassen Algorithm
void matmul(int *A, int *B, int *R, int n) {
if (n == 1) { Timing Analysis
(*R) += (*A) * (*B);
} else {
matmul(A, B, R, n/4);
matmul(A, B+(n/4), R+(n/4), n/4);
matmul(A+2*(n/4), B, R+2*(n/4), n/4);
matmul(A+2*(n/4), B+(n/4), R+3*(n/4), n/4);
matmul(A+(n/4), B+2*(n/4), R, n/4);
matmul(A+(n/4), B+3*(n/4), R+(n/4), n/4);
matmul(A+3*(n/4), B+2*(n/4), R+2*(n/4), n/4);
matmul(A+3*(n/4), B+3*(n/4), R+3*(n/4), n/4);
} Divide matrices in
sub-matrices and
recursively multiply
sub-matrices
A short list of categories of
Algorithms
• Algorithm types we will consider include:
• Simple recursive algorithms
• Backtracking algorithms
• Divide and conquer algorithms
• Dynamic programming algorithms
• Greedy algorithms
• Branch and bound algorithms
• Brute force algorithms
• Randomized algorithms
Simple recursive algorithms
• A simple recursive algorithm:
• Solves the base cases directly
• Recurs with a simpler subproblem
• Does some extra work to convert the solution to the simpler
subproblem into a solution to the given problem
• I call these “simple” because several of the other
algorithm types are inherently recursive
Example recursive algorithms
• To count the number of elements in a list:
• If the list is empty, return zero; otherwise,
• Step past the first element, and count the remaining elements
in the list
• Add one to the result
• To test if a value occurs in a list:
• If the list is empty, return false; otherwise,
• If the first thing in the list is the given value, return true;
otherwise
• Step past the first element, and test whether the value occurs
in the remainder of the list
Backtracking algorithms
• Backtracking algorithms are based on a depth-first
recursive search
• A backtracking algorithm:
• Tests to see if a solution has been found, and if so, returns it;
otherwise
• For each choice that can be made at this point,
• Make that choice
• Recur
• If the recursion returns a solution, return it
• If no choices remain, return failure
Example backtracking algorithm
• To color a map with no more than four colors:
• color(Country n):
• If all countries have been colored (n > number of countries) return
success; otherwise,
• For each color c of four colors,
• If country n is not adjacent to a country that has been
colored c
• Color country n with color c
• recursively color country n+1
• If successful, return success
• If loop exits, return failure
Divide and Conquer
• A divide and conquer algorithm consists of two parts:
• Divide the problem into smaller subproblems of the same
type, and solve these subproblems recursively
• Combine the solutions to the subproblems into a solution to
the original problem
• Traditionally, an algorithm is only called “divide and
conquer” if it contains at least two recursive calls
Examples
• Quicksort:
• Partition the array into two parts (smaller numbers in one
part, larger numbers in the other part)
• Quicksort each of the parts
• No additional work is required to combine the two sorted
parts
• Mergesort:
• Cut the array in half, and mergesort each half
• Combine the two sorted arrays into a single sorted array by
merging them
Binary tree lookup
• Here’s how to look up something in a sorted binary tree:
• Compare the key to the value in the root
• If the two values are equal, report success
• If the key is less, search the left subtree
• If the key is greater, search the right subtree
• This is not a divide and conquer algorithm because, although there are two recursive
calls, only one is used at each level of the recursion
• Fibonacci numbers
• To find the nth Fibonacci number:
• If n is zero or one, return one; otherwise,
• Compute fibonacci(n-1) and fibonacci(n-2)
• Return the sum of these two numbers
P1 3 10 15
P2
5 11 18
P3
6 14 20
P1 20 14
P2
18 11 5
P3
15 10 6 3
By
Dr. Swagata Mandal
Assistant Professor, Department of Electronics
and Communication
Jalpaiguri Government Engineering College
Dynamic Programming
• An algorithm design technique (like divide and
conquer)
2
Dynamic Programming
• Applicable when subproblems are not independent
– Subproblems share subsubproblems
E.g.: Combinations:
n n-1 n-1
= +
k k k-1
n n
=1 =1
1 n
3
Example: Combinations
Comb (6,4)
= 3 + 2 + 1 + 2 + 1 + 1 + 2 + 1 + 1 + 1
n n-1 n-1
= +
k k k-1
4
Dynamic Programming
• Used for optimization problems
– A set of choices must be made to get an optimal
solution
– Find a solution with the optimal value (minimum or
maximum)
– There may be many solutions that lead to an optimal
value
– Our goal: find an optimal solution
5
Dynamic Programming Algorithm
1. Characterize the structure of an optimal
solution
2. Recursively define the value of an optimal
solution
3. Compute the value of an optimal solution in a
bottom-up fashion
4. Construct an optimal solution from computed
information (not always necessary)
6
Assembly Line Scheduling
• Automobile factory with two assembly lines
– Each line has n stations: S1,1, . . . , S1,n and S2,1, . . . , S2,n
– Corresponding stations S1, j and S2, j perform the same function
but can take different amounts of time a1, j and a2, j
– Entry times are: e1 and e2; exit times are: x1 and x2
7
Assembly Line Scheduling
• After going through a station, can either:
– stay on same line at no cost, or
– transfer to other line: cost after Si,j is ti,j , j = 1, . . . , n - 1
8
Assembly Line Scheduling
• Problem:
what stations should be chosen from line 1 and which
from line 2 in order to minimize the total time through the
factory for one car?
9
One Solution
• Brute force
– Enumerate all possibilities of selecting stations
– Compute how long it takes in each case and choose
the best one
• Solution: 1 2 3 4 n
1 0 0 1 1
10
1. Structure of the Optimal Solution
11
1. Structure of the Optimal Solution
• Let’s consider all possible ways to get from the
starting point through station S1,j
– We have two choices of how to get to S1, j:
• Through S1, j - 1, then directly to S1, j
• Through S2, j - 1, then transfer over to S1, j
S1,j-1 S1,j
Line 1 a1,j-1 a1,j
t2,j-1
Line 2 a2,j-1
S2,j-1
12
1. Structure of the Optimal Solution
• Suppose that the fastest way through S1, j is
through S1, j – 1
– We must have taken a fastest way from entry through S1, j – 1
– If there were a faster way through S1, j - 1, we would use it instead
• Similarly for S2, j – 1
S1,j-1 S1,j
Line 1
a1,j-1 a1,j
a2,j-1
Line 2
S2,j-1
13
Optimal Substructure
• Generalization: an optimal solution to the
problem “find the fastest way through S1, j” contains
within it an optimal solution to subproblems: “find
the fastest way through S1, j - 1 or S2, j – 1”.
15
2. A Recursive Solution (cont.)
• Definitions:
– f* : the fastest time to get through the entire factory
– fi[j] : the fastest time to get from the starting point through station Si,j
16
2. A Recursive Solution (cont.)
• Base case: j = 1, i=1,2 (getting through station 1)
f1[1] = e1 + a1,1
f2[1] = e2 + a2,1
17
2. A Recursive Solution (cont.)
• General Case: j = 2, 3, …,n, and i = 1, 2
• Fastest way through S1, j is either:
– the way through S1, j - 1 then directly through S1, j, or
f1[j - 1] + a1,j
– the way through S2, j - 1, transfer from line 2 to line 1, then through S1, j
f2[j -1] + t2,j-1 + a1,j
S1,j-1 S1,j
Line 1
a1,j-1 a1,j
a2,j-1
Line 2
S2,j-1
18
2. A Recursive Solution (cont.)
e1 + a1,1 if j = 1
f1[j] =
min(f1[j - 1] + a1,j ,f2[j -1] + t2,j-1 + a1,j) if j ≥ 2
e2 + a2,1 if j = 1
f2[j] =
min(f2[j - 1] + a2,j ,f1[j -1] + t1,j-1 + a2,j) if j ≥ 2
19
3. Computing the Optimal Solution
f* = min (f1[n] + x1, f2[n] + x2)
f1[j] = min(f1[j - 1] + a1,j ,f2[j -1] + t2,j-1 + a1,j)
f2[j] = min(f2[j - 1] + a2,j ,f1[j -1] + t1,j-1 + a2,j)
1 2 3 4 5
f1[j] f1(1) f1(2) f1(3) f1(4) f1(5)
4 times 2 times
20
3. Computing the Optimal Solution
• For j ≥ 2, each value fi[j] depends only on the
values of f1[j – 1] and f2[j - 1]
• Idea: compute the values of fi[j] as follows:
in increasing order of j
1 2 3 4 5
f1[j]
f2[j]
• Bottom-up approach
– First find optimal solutions to subproblems
– Find an optimal solution to the problem from the
subproblems 21
Example
e1 + a1,1, if j = 1
f1[j] = min(f1[j - 1] + a1,j ,f2[j -1] + t2,j-1 + a1,j) if j ≥ 2
1 2 3 4 5
f1[j] 9 18[1] 20[2] 24[1] 32[1]
f* = 35[1]
f2[j] 12 16[1] 22[2] 25[1] 30[2]
22
FASTEST-WAY(a, t, e, x, n)
1. f1[1] ← e1 + a1,1
2. f2[1] ← e2 + a2,1 Compute initial values of f1 and f2
3. for j ← 2 to n
O(N)
4. do if f1[j - 1] + a1,j ≤ f2[j - 1] + t2, j-1 + a1, j
5. then f1[j] ← f1[j - 1] + a1, j
Compute the values of
6. l1[j] ← 1 f1[j] and l1[j]
7. else f1[j] ← f2[j - 1] + t2, j-1 + a1, j
8. l1[j] ← 2
9. if f2[j - 1] + a2, j ≤ f1[j - 1] + t1, j-1 + a2, j
10. then f2[j] ← f2[j - 1] + a2, j
Compute the values of
11. l2[j] ← 2 f2[j] and l2[j]
12. else f2[j] ← f1[j - 1] + t1, j-1 + a2, j
13. l2[j] ← 1
23
FASTEST-WAY(a, t, e, x, n) (cont.)
14. if f1[n] + x1 ≤ f2[n] + x2
15. then f* = f1[n] + x1
Compute the values of
16. l* = 1 the fastest time through the
17. else f* = f2[n] + x2 entire factory
18. l* = 2
24
4. Construct an Optimal Solution
Alg.: PRINT-STATIONS(l, n)
i ← l*
print “line ” i “, station ” n
for j ← n downto 2
do i ←li[j]
print “line ” i “, station ” j - 1
1 2 3 4 5
f1[j]/l1[j] 9 18[1] 20[2] 24[1] 32[1]
l* = 1
f2[j]/l2[j] 12 16[1] 22[2] 25[1] 30[2]
25
Matrix-Chain Multiplication
Problem: given a sequence A1, A2, …, An,
compute the product:
A1 A2 An
• Matrix compatibility:
C=AB C=A1 A2 Ai Ai+1 An
colA = rowB coli = rowi+1
rowC = rowA rowC = rowA1
colC = colB colC = colAn
26
MATRIX-MULTIPLY(A, B)
if columns[A] rows[B]
then error “incompatible dimensions”
else for i 1 to rows[A]
do for j 1 to columns[B] rows[A] cols[A] cols[B]
do C[i, j] = 0 multiplications
for k 1 to columns[A]
k do C[i, j] C[i, j] + A[i, k] B[k, j]
j cols[B]
j cols[B]
i = i
* k
A B C
rows[A]
rows[A] 27
Matrix-Chain Multiplication
• In what order should we multiply the matrices?
A1 A2 An
• Parenthesize the product to get the order in which
matrices are multiplied
• E.g.: A1 A2 A3 = ((A1 A2) A3)
= (A1 (A2 A3))
• Which one of these orderings should we choose?
– The order in which we multiply the matrices has a
significant impact on the cost of evaluating the product
28
Example
A1 A2 A3
• A1: 10 x 100
• A2: 100 x 5
• A3: 5 x 50
1. ((A1 A2) A3): A1 A2 = 10 x 100 x 5 = 5,000 (10 x 5)
((A1 A2) A3) = 10 x 5 x 50 = 2,500
Total: 7,500 scalar multiplications
2. (A1 (A2 A3)): A2 A3 = 100 x 5 x 50 = 25,000 (100 x 50)
(A1 (A2 A3)) = 10 x 100 x 50 = 50,000
Total: 75,000 scalar multiplications
one order of magnitude difference!!
29
Matrix-Chain Multiplication:
Problem Statement
• Given a chain of matrices A1, A2, …, An, where
Ai has dimensions pi-1x pi, fully parenthesize the
product A1 A2 An in a way that minimizes the
number of scalar multiplications.
A1 A2 Ai Ai+1 An
p0 x p1 p1 x p2 pi-1 x pi pi x pi+1 pn-1 x pn
30
1. The Structure of an Optimal
Parenthesization
• Notation:
Ai…j = Ai Ai+1 Aj, i j
31
Optimal Substructure
Ai…j = Ai…k Ak+1…j
32
2. A Recursive Solution
• Subproblem:
33
2. A Recursive Solution
• Consider the subproblem of parenthesizing
Ai…j = Ai Ai+1 Aj for 1 i j n
pi-1pkpj
= Ai…k Ak+1…j for i k < j
m[i, k] m[k+1,j]
• Assume that the optimal parenthesization splits
the product Ai Ai+1 Aj at k (i k < j)
35
3. Computing the Optimal Costs
0 if i = j
m[i, j] = min {m[i, k] + m[k+1, j] + pi-1pkpj} if i < j
ik<j
37
3. Computing the Optimal Costs (cont.)
0 if i = j
m[i, j] = min {m[i, k] + m[k+1, j] + pi-1pkpj} if i < j
ik<j
• Length = 1: i = j, i = 1, 2, …, n
• Length = 2: j = i + 1, i = 1, 2, …, n-1
1 2 3 n
n
m[1, n] gives the optimal
solution to the problem
j
Compute rows from bottom to top 3
and from left to right 2
1
i 38
Example: min {m[i, k] + m[k+1, j] + pi-1pkpj}
m[2, 2] + m[3, 5] + p1p2p5 k=2
1 2 3 4 5 6
6
5
• Values m[i, j] depend only
4
j on values that have been
3
previously computed
2
1
i
39
Example min {m[i, k] + m[k+1, j] + pi-1pkpj}
1 2 3
Compute A1 A2 A3 2
3 2
7500 25000 0
• A1: 10 x 100 (p0 x p1)
1
2 0
• A2: 100 x 5 (p1 x p2) 5000
m[i, i] = 0 for i = 1, 2, 3
m[1, 2] = m[1, 1] + m[2, 2] + p0p1p2 (A1A2)
= 0 + 0 + 10 *100* 5 = 5,000
m[2, 3] = m[2, 2] + m[3, 3] + p1p2p3 (A2A3)
= 0 + 0 + 100 * 5 * 50 = 25,000
m[1, 3] = min m[1, 1] + m[2, 3] + p0p1p3 = 75,000 (A1(A2A3))
m[1, 2] + m[3, 3] + p0p2p3 = 7,500 ((A1A2)A3)
40
Matrix-Chain-Order(p)
O(N3)
41
4. Construct the Optimal Solution
• In a similar matrix s we
keep the optimal 1 2 3 n
values of k n
• s[i, j] = a value of k
such that an optimal k
j
parenthesization of 3
Ai..j splits the product 2
between Ak and Ak+1 1
42
4. Construct the Optimal Solution
• s[1, n] is associated with
the entire product A1..n
1 2 3 n
– The final matrix
n
multiplication will be split
at k = s[1, n]
A1..n = A1..s[1, n] As[1, n]+1..n
j
– For each subproduct 3
recursively find the 2
corresponding value of k 1
that results in an optimal
parenthesization
43
4. Construct the Optimal Solution
• s[i, j] = value of k such that the optimal
parenthesization of Ai Ai+1 Aj splits the
product between Ak and Ak+1
1 2 3 4 5 6
6 3 3 3 5 5 -
• s[1, n] = 3 A1..6 = A1..3 A4..6
5 3 3 3 4 -
• s[1, 3] = 1 A1..3 = A1..1 A2..3
4 3 3 3 -
• s[4, 6] = 5 A4..6 = A4..5 A6..6
3 1 2 -
j
2 1 -
1 -
i
44
4. Construct the Optimal Solution (cont.)
PRINT-OPT-PARENS(s, i, j)
1 2 3 4 5 6
if i = j 6 3 3 3 5 5 -
then print “A”i 5 3 3 3 4 -
else print “(” 4 3 3 3 -
j
3 1 2 -
PRINT-OPT-PARENS(s, i, s[i, j])
2 1 -
PRINT-OPT-PARENS(s, s[i, j] + 1, j)
1 -
print “)”
i
45
Example: A1 A6 ( ( A1 ( A2 A3 ) ) ( ( A4 A5 ) A6 ) )
1. n length[p] – 1
2. for i 1 to n
Initialize the m table with
3. do for j i to n large values that indicate
whether the values of m[i, j]
have been computed
4. do m[i, j]
48
Memoized Matrix-Chain
Alg.: LOOKUP-CHAIN(p, i, j) Running time is O(n3)
1. if m[i, j] <
2. then return m[i, j]
3. if i = j
4. then m[i, j] 0
5. else for k i to j – 1
6. do q LOOKUP-CHAIN(p, i, k) +
LOOKUP-CHAIN(p, k+1, j) + pi-1pkpj
7. if q < m[i, j]
8. then m[i, j] q
9. return m[i, j] 49
Dynamic Progamming vs. Memoization
50
Elements of Dynamic Programming
• Optimal Substructure
– An optimal solution to a problem contains within it an
optimal solution to subproblems
– Optimal solution to the entire problem is build in a
bottom-up manner from optimal solutions to
subproblems
• Overlapping Subproblems
– If a recursive algorithm revisits the same subproblems
over and over the problem has overlapping
subproblems
52
Parameters of Optimal Substructure
53
Parameters of Optimal Substructure
• Intuitively, the running time of a dynamic
programming algorithm depends on two factors:
– Number of subproblems overall
– How many choices we look at for each subproblem
• Assembly line
– (n) subproblems (n stations) (n) overall
– 2 choices for each subproblem
• Matrix multiplication:
– (n2) subproblems (1 i j n)
(n3) overall
– At most n-1 choices
54
Longest Common Subsequence
• Given two sequences
X = x1, x2, …, xm
Y = y1, y2, …, yn
find a maximum length common subsequence
(LCS) of X and Y
• E.g.:
X = A, B, C, B, D, A, B
• Subsequences of X:
– A subset of elements in the sequence taken in order
A, B, D, B, C, D, B, etc.
55
Example
X = A, B, C, B, D, A, B X = A, B, C, B, D, A, B
Y = B, D, C, A, B, A Y = B, D, C, A, B, A
56
Brute-Force Solution
• For every subsequence of X, check whether it’s
a subsequence of Y
57
Making the choice
X = A, B, D, E
Y = Z, B, E
• Choice: include one element into the common
sequence (E) and solve the resulting
subproblem
X = A, B, D, G
Y = Z, B, D
• Choice: exclude an element from a string and
solve the resulting subproblem
58
Notations
• Given a sequence X = x1, x2, …, xm we define
the i-th prefix of X, for i = 0, 1, 2, …, m
59
A Recursive Solution
Case 1: xi = yj
e.g.: Xi = A, B, D, E
Yj = Z, B, E
c[i, j] = c[i - 1, j - 1] + 1
60
A Recursive Solution
Case 2: xi yj
e.g.: Xi = A, B, D, G
Yj = Z, B, D
c[i, j] = max { c[i - 1, j], c[i, j-1] }
– Must solve two problems
• find a LCS of Xi-1 and Yj: Xi-1 = A, B, D and Yj = Z, B, D
• find a LCS of Xi and Yj-1: Xi = A, B, D, G and Yj = Z, B
69
Improving the Code
• If we only need the length of the LCS
– LCS-LENGTH works only on two rows of c at a time
70
Introduction to trees
and graphs
By
Dr. Swagata Mandal
Electronics and Communication Department
Jalpaiguri Government Engineering College
Trees
What is a tree?
• Trees are structures used to represent
hierarchical relationship
• Each tree consists of nodes and edges
• Each node represents an object
• Each edge represents the relationship between
two nodes.
node
edge
3
Some applications of Trees
Organization Chart Expression Tree
President
+
VP VP
Personnel Marketing * 5
3 2
Director Director
Customer Sales
Relation
4
Terminology I
• For any two nodes u and v, if there is an edge
pointing from u to v, u is called the parent of v
while v is called the child of u. Such edge is
denoted as (u, v).
• In a tree, there is exactly one node without
parent, which is called the root. The nodes
without children are called leaves.
root
u
u: parent of v
v: child of u
v
leaves 5
Terminology II
• In a tree, the nodes without children are
called leaves. Otherwise, they are called
internal nodes.
internal nodes
leaves
6
Terminology III
• If two nodes have the same parent, they are
siblings.
• A node u is an ancestor of v if u is parent of v or
parent of parent of v or …
• A node v is a descendent of u if v is child of v or
child of child of v or …
u
T
A subtree of T
v v
8
Terminology V
• Level of a node n: number of nodes on the path from
root to node n
• Height of a tree: maximum level among all of its node
Level 1
Level 2
height=4
n Level 3
Level 4
9
Binary Tree
• Binary Tree: Tree in which every node has at
most 2 children
• Left child of u: the child on the left of u
• Right child of u: the child on the right of u
x: left child of u
u v y: right child of u
w: right child of v
x y z: left child of w
w
z
10
Full binary tree
• If T is empty, T is a full binary tree of height 0.
• If T is not empty and of height h >0, T is a full
binary tree if both subtrees of the root of T are
full binary trees of height h-1.
11
Property of binary tree (I)
• A full binary tree of height h has 2h-1
nodes
No. of nodes = 20 + 21 + … + 2(h-1)
= 2h – 1
Level 1: 20 nodes
Level 2: 21 nodes
Level 3: 22 nodes
12
Property of binary tree (II)
• Consider a binary tree T of height h. The
number of nodes of T 2h-1
13
Property of binary tree (III)
• The minimum height of a binary tree with n
nodes is log(n+1)
14
Representation of a Binary Tree
• An array-based representation
• A reference-based representation
15
An array-based representation
nodeNum item leftChild rightChild
–1: empty tree root
0 d 1 2 0
1 b 3 4
2 f 5 -1
3 a -1 -1
d 4 c -1 -1
5 e -1 -1
b f 6 ? ? ? free
7 ? ? ? 6
8 ? ? ?
a c e 9 ? ? ?
... ..... ..... ....
16
Reference Based
Representation
NULL: empty tree left element right
b f
b f
a c
a c
17
Tree Traversal
• Given a binary tree, we may like to do
some operations on all nodes in a binary
tree. For example, we may want to double
the value in every node in a binary tree.
• To do this, we need a traversal algorithm
which visits every node in the binary tree.
18
Ways to traverse a tree
• There are three main ways to traverse a tree:
– Pre-order:
• (1) visit node, (2) recursively visit left subtree, (3) recursively
visit right subtree
– In-order:
• (1) recursively visit left subtree, (2) visit node, (3) recursively
right subtree
– Post-order:
• (1) recursively visit left subtree, (2) recursively visit right
subtree, (3) visit node
– Level-order:
• Traverse the nodes level by level
• In different situations, we use different traversal
algorithm.
19
Examples for expression tree
• By pre-order, (prefix)
+*23/84
• By in-order, (infix) +
2*3+8/4
• By post-order, (postfix) * /
23*84/+ 2 3 8 4
• By level-order,
+*/2384
• Note 1: Infix is what we read!
• Note 2: Postfix expression can be computed
efficiently using stack
20
Graphs
21
What is a graph?
• Graphs represent the relationships among data
items
• A graph G consists of
– a set V of nodes (vertices)
– a set E of edges: each edge connects two nodes
• Each node represents an item
• Each edge represents the relationship between
two items
node
edge
22
Examples of graphs
Molecular Structure Computer Network
H Server 1 Terminal 1
H C H
Terminal 2
H Server 2
23
Formal Definition of graph
• The set of nodes is denoted as V
• For any nodes u and v, if u and v are
connected by an edge, such edge is denoted
as (u, v) v
(u, v)
u
• The set of edges is denoted as E
• A graph G is defined as a pair (V, E)
24
Adjacent
• Two nodes u and v are said to be adjacent
if (u, v) E
v
(u, v)
u
w
u and v are adjacent
v and w are not adjacent
25
Path and simple path
• A path from v1 to vk is a sequence of
nodes v1, v2, …, vk that are connected by
edges (v1, v2), (v2, v3), …, (vk-1, vk)
• A path is called a simple path if every
node appears at most once.
v2 v
v1 3
v4 v5
This is a connected graph because there exists path
between every pair of nodes
28
Example of disconnected graph
v1 v3 v7 v8
v2
v4 v5
v6 v9
29
Connected component
• If a graph is disconnect, it can be partitioned into
a number of graphs such that each of them is
connected. Each such graph is called a
connected component.
v2 v7 v8
v1 v3
v4 v5
v6 v9
30
Complete graph
• A graph is complete if each pair of distinct
nodes has an edge
31
Subgraph
• A subgraph of a graph G =(V, E) is a
graph H = (U, F) such that U V and
F E.
v2 v2
v1 v3 v3
v4 v5 v4 v5
G H
32
Weighted graph
• If each edge in G is assigned a weight, it is
called a weighted graph
3500
2000
Houston
33
Directed graph (digraph)
• All previous graphs are undirected graph
• If each edge in E has a direction, it is called a directed
edge
• A directed graph is a graph where every edges is a
directed edge
Chicago 1000 New York
Directed edge
2000 3500
Houston
34
More on directed graph
x y
35
Multigraph
• A graph cannot have duplicate edges.
• Multigraph allows multiple edges and self
edge (or loop).
36
Property of graph
• A undirected graph that is connected and
has no cycle is a tree.
• A tree with n nodes have exactly n-1
edges.
• A connected undirected graph with n
nodes must have at least n-1 edges.
37
Implementing Graph
• Adjacency matrix
– Represent a graph using a two-dimensional
array
• Adjacency list
– Represent a graph using n linked lists where
n is the number of vertices
38
Adjacency matrix for directed graph
Matrix[i][j] = 1 if (vi, vj)E 1 2 3 4 5
0 if (vi, vj)E
v1 v2 v3 v4 v5
v2 1 v1 0 1 0 0 0
v1 v3 2 v 0 0 0 1 0
2
3 v3 0 1 0 1 0
v4 v5 4 v4 0 0 0 0 0
5 v5 0 0 1 1 0
G
39
Adjacency matrix for weighted
undirected graph
Matrix[i][j] = w(vi, vj) if (vi, vj)E or (vj, vi)E
∞ otherwise
1 2 3 4 5
v2 v1 v2 v3 v4 v5
v1 2 v3
5 1 v1 ∞ 5 ∞ ∞ ∞
4 3 7 2 v2 5 ∞ 2 4 ∞
v4
8 v5 3 v3 0 2 ∞ 3 7
4 v4 ∞ 4 3 ∞ 8
G
5 v5 ∞ ∞ 7 8 ∞
40
Adjacency list for directed graph
1 v1 v2
v2 2 v2 v4
v1 v3
3 v3 v2 v4
4 v4
v4 v5
5 v5 v3 v4
G
41
Adjacency list for weighted
undirected graph
v2
v1 2 v3 1 v1 v2(5)
5
4 2 v2 v1(5) v3(2) v4(4)
3 7
3 v3 v2(2) v4(3) v5(7)
v4
8 v5 4 v4 v2(4) v3(3) v5(8)
5 v5 v3(7) v4(8)
G
42
Pros and Cons
• Adjacency matrix
– Allows us to determine whether there is an
edge from node i to node j in O(1) time
• Adjacency list
– Allows us to find all nodes adjacent to a given
node j efficiently
– If the graph is sparse, adjacency list requires
less space
43
Thank You
44
Elementary Graph Algorithms
A graph G having V no of vertices and E no. of edges can be denoted as G(V,E) and can be
represented as a collection of adjacency lists or as an adjacency matrix.
Fig1: Two representations of an undirected graph. (a) An undirected graph G with 5 vertices and 7
edges. (b) An adjacency-list representation of G. (c) The adjacency-matrix representation of G.
Fig 2: Two representations of a directed graph. (a) A directed graph G with 6 vertices and 8 edges. (b)
An adjacency-list representation of G. (c) The adjacency-matrix representation of G.
The adjacency-list representation of a graph G = (V,E) consists of an array Adj of | | lists, one for
each vertex in V . For each , the adjacency list [ ] contains all the vertices such that there
is an edge . That is, [ ] consists of all the vertices adjacent to u in G. Since the
adjacency lists represent the edges of a graph, in pseudocode we treat the array Adj as an attribute of
the graph, just as we treat the edge set E. In pseudocode, therefore, we will see notation such as
[ ]. Figure 1(b) is an adjacency-list representation of the undirected graph in Figure 1(a).
Similarly, Figure 2(b) is an adjacency-list representation of the directed graph in Figure 2(a).
If G is a directed graph, the sum of the lengths of all the adjacency lists is | |, since an edge of the
form (u,v) is represented by having v appear in [ ]. If G is an undirected graph, the sum of the
lengths of all the adjacency lists is 2| |, since if (u,v) is an undirected edge, then u appears in v’s
adjacency list and vice versa. For both directed and undirected graphs, the adjacency-list
representation has the desirable property that the amount of memory it requires is .
We can readily adapt adjacency lists to represent weighted graphs, that is, graphs for which each edge
has an associated weight, typically given by a weight function . For example, let G = (V, E)
be a weighted graph with weight function w. We simply store the weight w(u,v) of the edge
with vertex v in u’s adjacency list. The adjacency-list representation is quite robust in that we can
modify it to support many other graph variants.
For the adjacency-matrix representation of a graph G = (V,E), we assume that the vertices are
numbered | | in some arbitrary manner. Then the adjacency-matrix representation of a graph G
consists of a | | | | matrix such that
Figures 1(c) and 2(c) are the adjacency matrices of the undirected and directed graphs in
Figures 1(a) and 2(a), respectively. The adjacency matrix of a graph requires ‚
memory, independent of the number of edges in the graph.
Since in an undirected graph, and represent the same edge, the adjacency matrix A
of an undirected graph is its own transpose: .
Like the adjacency-list representation of a graph, an adjacency matrix can represent a weighted graph.
For example, if G = (V,E) is a weighted graph with edge-weight function w, we can simply store the
weight of the edge as the entry in row u and column v of the adjacency matrix. If
an edge does not exist, we can store a NIL value as its corresponding matrix entry, though for many
problems it is convenient to use a value such as 0 or .
Breadth-first search is so named because it expands the frontier between discovered and undiscovered
vertices uniformly across the breadth of the frontier. That is, the algorithm discovers all vertices at
distance k from s before discovering any vertices at distance k+1.
To keep track of progress, BFS colours each vertex white, grey, or black. All vertices start out white
and may later become grey and then black. A vertex is discovered the first time it is encountered
during the search, at which time it becomes non-white i.e it will be marked with either grey or black.
If and vertex u is black, then vertex v is either grey or black; that is, all vertices adjacent to
black vertices have been discovered. Grey vertices may have some adjacent white vertices; they
represent the frontier between discovered and undiscovered vertices.
BFS constructs a breadth-first tree, initially containing only its root, which is the source vertex s.
Whenever the search discovers a white vertex v in the course of scanning the adjacency list of an
already discovered vertex u, the vertex v and the edge (u,v) are added to the tree. We say that u is the
predecessor or parent of v in the breadth-first tree. Since a vertex is discovered at most once, it has at
most one parent. Ancestor and descendant relationships in the breadth-first tree are defined relative to
the root s as usual: if u is on the simple path in the tree from the root s to vertex v, then u is an
ancestor of v and v is a descendant of u.
The breadth-first-search procedure BFS below assumes that the input graph G = (V,E) is represented
using adjacency lists. It attaches several additional attributes to each vertex in the graph. We store the
colour of each vertex in the attribute u:color and the predecessor of u in the attribute . If u
has no predecessor (for example, if u = s or u has not been discovered), then NIL. The attribute
u.d holds the distance from the source s to vertex u computed by the algorithm. The algorithm also
uses a first-in, first-out queue Q to manage the set of grey vertices. Pseudo code of BFS is given
below:
BFS (G,s)
1. for each vertex
2. [Link] = WHITE
3.
4. = NIL
5.
6.
7. NIL
8.
9. ENQUEUE(Q,s)
10. While
11. u = DEQUEUE(Q)
12. for each [ ]
13. if
14.
15. v.d = u.d + 1
16.
17. ENQUEUE(Q,v)
18. [Link] = BLACK
With the exception of the source vertex s, lines 1–4 paint every vertex white, set u.d to be infinity for
each vertex u, and set the parent of every vertex to be NIL. Line 5 paints s grey, since we consider it
to be discovered as the procedure begins. Line 6 initializes s.d to 0, and line 7 sets the predecessor of
the source to be NIL. Lines 8–9 initialize Q to the queue containing just the vertex s.
The while loop of lines 10–18 iterates as long as there remain grey vertices, which are discovered
vertices that have not yet had their adjacency lists fully examined.
Fig 3 shows the example of the breadth first search on the graph shown in Figure 3 (a). Here the
starting node is s and the node which is already traversed is marked with black colour and their
adjacent nodes are marked with grey colour. During the graph traversal, adjacent node of each
traversed node is pushed into the queue using the function ENQUEUE. When that nodes has traversed
they will be deleted from queue using DEQUEUE function.
Fig 3: The operation of BFS on an undirected graph. Tree edges are shown shaded as they are
produced by BFS. The value of u.d appears within each vertex u. The queue Q is shown at the
beginning of each iteration of the while loop of lines 10–18. Vertex distances appear below vertices in
the queue.
Shortest paths: At the beginning of this section, we claimed that breadth-first search finds the
distance to each reachable vertex in a graph G = (V,E) from a given source vertex . Define the
shortest-path distance from s to v as the minimum number of edges in any path from vertex s
to vertex v; if there is no path from s to v, then . We call a path of length from s to
v a shortest path from s to v.
Lemma 1: Let G = (V,E) be a directed or undirected graph, and suppose that BFS is run on G from a
given source vertex . Then upon termination, for each vertex , the value v.d computed by
BFS satisfies
Breadth-first Trees: The procedure BFS builds a breadth-first tree as it searches the
graph, as Figure 3 in my last note. The tree corresponds to the π attributes. More formally, for
a graph G = (V,E) with source s, we define the predecessor sub-graph of G as
where { } { } and { { }}.
The following procedure prints out the vertices on a shortest path from s to v, as BFS has
already computed a breadth-first tree:
PRINT-PATH (G,s,v)
1. if
2. print s
3. elseif
4. print “no path from” s “to” v “exists”
5. else PRINT-PATH
6. print v.
The strategy followed by depth-first search is, as its name implies, to search “deeper” in the
graph whenever possible. Depth-first search explores edges out of the most recently
discovered vertex v that still has unexplored edges leaving it. Once all of v’s edges have been
explored, the search “backtracks” to explore edges leaving the vertex from which v was
discovered. This process continues until we have discovered all the vertices that are reachable
from the original source vertex. If any undiscovered vertices remain, then depth-first search
selects one of them as a new source, and it repeats the search from that source. The algorithm
repeats this entire process until it has discovered every vertex.
DFS Algorithm:
A standard DFS implementation puts each vertex of the graph into one of two categories:
1. Visited
2. Not Visited
The purpose of the algorithm is to mark each vertex as visited while avoiding cycles.
The DFS algorithm works as follows:
Example:
Step1: Initialize the Stack
Step2 : Mark S as visited and put it onto the stack. Explore any unvisited adjacent node from
S. We have three nodes and we can pick any of them. For this example, we shall take the
node in an alphabetical order.
Step 3: Mark A as visited and put it onto the stack. Explore any unvisited adjacent node from
A. Both S and D are adjacent to A but we are concerned for unvisited nodes only.
Step 4: Visit D and mark it as visited and put onto the stack. Here, we have B and C nodes,
which are adjacent to D and both are unvisited. However, we shall again choose in an
alphabetical order.
Step 5: We choose B, mark it as visited and put onto the stack. Here B does not have any
unvisited adjacent node. So, we pop B from the stack.
Step 6: We check the stack top for return to the previous node and check if it has any
unvisited nodes. Here, we find D to be on the top of the stack.
Step 7: Only unvisited adjacent node is from D is C now. So we visit C, mark it as visited and
put it onto the stack.
As C does not have any unvisited adjacent node so we keep popping the stack until we find a
node that has an unvisited adjacent node. In this case, there's none and we keep popping until
the stack is empty.
DFS(G, u)
[Link] = true
for each v [Link][u]
if [Link] == false
DFS(G,v)
init() {
For each u G
[Link] = false
For each u G
DFS(G, u)
}
A Preordering is a list of the vertices in the order that they were first visited by the
depth-first search algorithm. This is a compact and natural way of describing the
progress of the search. A preordering of an expression tree is the expression in Polish
notation.
A Postordering is a list of the vertices in the order that they were last visited by the
algorithm. A postordering of an expression tree is the expression in reverse Polish
notation.
A Reverse Preordering is the reverse of a preordering, i.e. a list of the vertices in the
opposite order of their first visit. Reverse preordering is not the same as postordering.
A Reverse Postordering is the reverse of a postordering, i.e. a list of the vertices in
the opposite order of their last visit. Reverse postordering is not the same as
preordering.
In the above example when searching the directed graph below beginning at node A, the
sequence of traversals is either A B D B A C A or A C D C A B A (choosing to first visit B
or C from A is up to the algorithm). Note that repeat visits in the form of backtracking to a
node, to check if it has still unvisited neighbours, are included here (even if it is found to have
none). Thus the possible preorderings are A B D C and A C D B, while the possible
postorderings are D B C A and D C B A, and the possible reverse postorderings are A C B D
and A B C D.
Assignment
Write a C/Python or any other language you know for depth first search of the following
graph taking 4 as the starting node? Also find the pre-ordering and post-ordering output for
DFS traversal of the following graph taking 2 as starting node.
1 5
2 7
4
3 8
10 9
Topological Sorting
A topological sort of a Directed Acyclic Graph (DAG) G= (V,E) is a linear ordering of all its
vertices such that if G contains an edge (u, v) then u appears before v in the ordering. (If the
graph contains a cycle, then no linear ordering is possible.) We can view a topological sort of
a graph as an ordering of its vertices along a horizontal line so that all directed edges go from
left to right.
Example:
2 3
4 5
Step1 : First calculate in-degree of each vertex i.e number of edges entered into a node.
(1)
2 3 (2)
(0)
4 5
(2) (1)
Step 2: Eliminate node have in-degree value ‘0’ and associated edges. Update in-degree value
of each node accordingly and print eliminated node.
2 3 (2)
(0)
Output: 1
4 5
(1) (1)
Step 3: Perform same operation on node 2
3 (1)
Output: 1,2
4 5
(0) (1)
Step 4: Perform same operation on node 4
3 (0)
Output: 1,2,4
5 (0)
Step 5: Now we can choose first node 3 and then node 5. Then output will be 1,2,4,3,5.
Otherwise we can choose node 5 first and then node 3. In this case output will be 1,2,4,5,3.
Now we will check if we select a directed graph which has cycle within it then it cannot
perform the topological sorting.
Example:
2 3
4 5
Step1: As in the previous case, calculate in-degree of each node in the graph.
(2)
2 3 (0)
(1)
4 5
(1) (1)
Step2: Eliminate node 3 and associated edge. Update in-degree value of node 2.
(1)
2
(1)
4 5
(1) (1)
Now in-degree value of each node is same i.e ‘1’ and no one have in-degree value 0. Hence,
we cannot proceed further.
while S is non-empty do
remove a node n from S
add n to tail of L
for each node m with an edge e from n to m do
remove edge e from the graph
if m has no other incoming edges then
insert m into S
1 2
4 3
As all four nodes in Fig 1 can be reached from any other nodes they are called strongly
connected Graph.
1 0 3
2 4
1) Compute Depth First Search on a graph G (DFS(G)) to compute finish time for each
vertex
2) Compute transpose of a graph G which is represented as
3) Call DFS( ) on vertices in decreasing order of their finish times.
4) Output: Vertices as separate SCC’s
Compute the transpose of a graph: Reverse the direction of edge in the graph keeping the
vertices as it is
1 2 1 2
4 3 4 3
Fig 2(a): Graph G (V,E) Fig 2(b): Transpose of the graph G(V,E): GT(V,E)
Kosaraju’s Algorithm:
Step 1: Create an empty stack ‘S’
Step 2: Do DFS traversal of the graph. In DFS traversal, after calling recursive DFS for
adjacent vertices of a vertex, push the vertex to stack.
Step 4: One by one pop a vertex from S while S is not empty. Let the popped vertex be ‘v’.
Take v as source and do DFS call on v. The DFS starting from v prints strongly connected
component of v.
0 3 5 6 7
1 2 4 8
Step 2: Start the DFS traversal from vertex ‘0’ and write start and stop time in each node
(7,10) (8,9)
(4,5) (15,18)
(1,14) 0 3 5 6 7
1 2 4 (6,11) 8 (16,17)
(2,13)
(3,12)
Start from node ‘0’ at t=1 and it has unvisited neighbourer. Go to node ‘1’ at t= 2 and it has
also unvisited neighbourer. Similarly go to node 2 and 3 at t = 3 and t = 4 respectively. As
neighbour node of 3 is already visited at t = 5, travel to node 3 will be finished and 3 will be
added to stack S.
S ={3,}
Now come back to node 2 which has still unvisited neighbour which is ‘4’. Visit vertex ‘4’ at
t = 6. Visit neighbour of node 4 i.e node 5 at t = 7. Similarly visit neighbour of node 5 i.e
node 6 at t =8. As node 6 has no neighbourer to visit, stop visit at node 6 and node 6 to stack
S at node t = 9.
S ={3,6}
Similarly, stop visit node 5 at t = 10, node 4 at t = 11 and add to the stack.
S ={3,6,5,4}
Obeying the same rule stop visit at node 2,1 and 0 at t= 12, t= 13 and t = 14 and add to the
stack S
S ={3,6,5,4,2,1,0}
As still some vertices are visited DFS will continue. Pick node 7 starts visiting at t = 15, and
it has neighbourer node 8 which will be visited at t = 16. As node 8 has no neighbourer to
visit, stop visit at t= 17 and add node 8 to stack S. Go back to node 7 and add it to stack at t =
18 as there is no neighbourer to visit. Contain of stack S will be
S ={3,6,5,4,2,1,0,8,7}
0 3 5 6 7
1 2 4 8
Step 4: Pop first element of the stack i.e 7. As vertex 7 has no neighbour node in GT to visit
so 7 itself form a SCC. Contain of the S will be S={3,6,5,4,2,1,0,8}
Pop 8 from the stack. As the node 8 has no unvisited neighbourer in GT, it will alone form a
SCC. Contain of the S will be S={3,6,5,4,2,1,0}
Next pop 0 from the stack. As from node 0 we can visit 3,2,1 using DFS in GT. So 0,3,2,1
will form SCC. So when we pop 1 and 2 from the stack it will not be considered as they are
already visited. Now content of S will be S = {3,6,5,4}.
Next pop 4 from stack S. Visit vertices 6 and 5 as neighbour node of 4 using DFS in GT. So
4,6,5 will form a SCC. So when we pop 5 and6 from the stack it will not be considered as
they are already visited. Now content of S will be S = {3}.
Next we will pop 3 but it will not be considered as it is already visited. As stack is already
empty we will reach at the terminating condition.
So finally we will get four SCC from graph G. They are {7},{8},{0,3,2,1} and {4,6,5}. It can
be represented as follows
0,3,2,1 4,6,5 8
7
Assignment
Find the SCC in the following directed graph
H I
A C E
B D F G J
K
Minimum Spanning Tree
What is a Spanning Tree?
Given an undirected and connected graph G= (V,E), a spanning tree of the graph is a tree that
spans (that is, it includes every vertex of ) and is a sub-graph of G (every edge in the tree
belongs to G). Spanning tree should be acyclic i.e it should not contain any cycle.
The cost of the spanning tree is the sum of the weights of all the edges in the tree. There can
be many spanning trees. Minimum spanning tree is the spanning tree where the cost is
minimum among all the spanning trees. There also can be many minimum spanning trees.
Minimum spanning tree has direct application in the design of networks. It is used in
algorithms approximating the travelling salesman problem, multi-terminal minimum cut
problem and minimum-cost weighted perfect matching. Other practical applications are:
1. Cluster Analysis
2. Handwriting recognition
3. Image segmentation
4. Electronic circuit design and automation
In the graph (G) nodes marked with yellow have more than one spanning tree as shown by
the graph (G’) with blue nodes. If G’ be a spanning tree and have vertices V’ and edges E’
then V’= V, and . Out of all the spanning tree the tree whose weighted
sum of all the edges is minimum will be minimum will be selected as minimum spanning
tree. Here the summation of the weight of all edges is minimum i.e 7 in the second spanning
tree so it will be selected as minimum spanning tree.
There are two famous algorithms for finding the Minimum Spanning Tree:
Kruskal’s Algorithm
Kruskal’s Algorithm builds the spanning tree by adding edges one by one into a growing
spanning tree. Kruskal's algorithm follows greedy approach as in each iteration it finds an
edge which has least weight and adds it to the growing spanning tree.
Algorithm Steps:
So now the question is how to check if two vertices are connected or not?
This could be done using DFS which starts from the first vertex, then check if the second
vertex is visited or not. But DFS will make time complexity large as it has an order of
where is the number of vertices, is the number of edges. So the best solution is
"Disjoint Sets".
Disjoint sets are sets whose intersection is the empty set so it means that they don't have any
element in common.
In Kruskal’s algorithm, at each iteration we will select the edge with the lowest weight. So,
we will start with the lowest weighted edge first i.e., the edges with weight 1. After that we
will select the second lowest weighted edge i.e., edge with weight 2. Notice these two edges
are totally disjoint. Now, the next edge will be the third lowest weighted edge i.e., edge with
weight 3, which connects the two disjoint pieces of the graph. Now, we are not allowed to
pick the edge with weight 4, that will create a cycle and we can’t have any cycles. So we will
select the fifth lowest weighted edge i.e., edge with weight 5. Now the other two edges will
create cycles so we will ignore them. In the end, we end up with a minimum spanning tree
with total cost 11 ( = 1 + 2 + 3 + 5).
Time Complexity: In Kruskal’s algorithm, most time consuming operation is sorting because
the total complexity of the Disjoint-Set operations will be , which is the overall
Time Complexity of the algorithm.
Prim’s Algorithm
Prim’s Algorithm also use Greedy approach to find the minimum spanning tree. In Prim’s
Algorithm we grow the spanning tree from a starting position. Unlike an edge in Kruskal's,
we add vertex to the growing spanning tree in Prim's.
Algorithm Steps:
Maintain two disjoint sets of vertices. One containing vertices that are in the growing
spanning tree and other that are not in the growing spanning tree.
Select the cheapest vertex that is connected to the growing spanning tree and is not in
the growing spanning tree and add it into the growing spanning tree. This can be done
using Priority Queues. Insert the vertices, that are connected to growing spanning tree,
into the Priority Queue.
Check for cycles. To do that, mark the nodes which have been already selected and
insert only those nodes in the Priority Queue that are not marked.
In Prim’s Algorithm, we will start with an arbitrary node (it doesn’t matter which one) and
mark it. In each iteration we will mark a new vertex that is adjacent to the one that we have
already marked. As a greedy algorithm, Prim’s algorithm will select the cheapest edge and
mark the vertex. So we will simply choose the edge with weight 1. In the next iteration we
have three options, edges with weight 2, 3 and 4. So, we will select the edge with weight 2
and mark the vertex. Now again we have three options, edges with weight 3, 4 and 5. But we
can’t choose edge with weight 3 as it is creating a cycle. So we will select the edge with
weight 4 and we end up with the minimum spanning tree of total cost 7 ( = 1 + 2 +4).
Fig 2: Example of Prim’s Algorithm
Assignment
1. Given a weighted undirected graph. Find the sum of weights of edges of a Minimum
Spanning Tree.
Input:
Given two integers N and M. N represents the number of vertices in the graph. M
represents the number of edges between any two vertices.
Then M lines follow, each line has three space separated integers , , where
and represents an edge from a vertex to a vertex and respresents the weight
of that edge.
Output:
Print the summation of edges weights in the MST.
Constraints:
Single-Source Shortest Paths
In a shortest-paths problem, we are given a weighted, directed graph G = (V, E), with weight
function mapping edges to real-valued weights. The weight w(p) of path
〈 〉 is the sum of the weights of its constituent edges:
( ) ∑ ( )
( ) { * ( ) +
A shortest path from vertex u to vertex v is then defined as any path p with weight ( )
( )
Here, we will focus on the single-source shortest-paths problem: given a graph G = (V, E),
we want to find a shortest path from a given source vertex to each vertex .The
algorithm for the single-source problem can solve many other problems, including the
following variants.
Single-pair shortest-path problem: Find a shortest path from u to v for given vertices u and
v. If we solve the single-source problem with source vertex u, we can solve this problem also.
Moreover, all known algorithms for this problem have the same worst-case asymptotic
running time as the best single-source algorithms.
All-pairs shortest-paths problem: Find a shortest path from u to v for every pair of vertices u
and v. Although we can solve this problem by running a singles ource algorithm once from
each vertex, we usually can solve it faster. Additionally, its structure is interesting in its own
right.
Negative-weight edges
Some instances of the single-source shortest-paths problem may include edges whose weights
are negative. If the graph G = (V,E) contains no negative weight cycles reachable from the
source s, then for all , the shortest-path weight ( ) remains well defined, even if it
has a negative value. If the graph contains a negative-weight cycle reachable from s,
however, shortest-path weights are not well defined. No path from s to a vertex on the cycle
can be a shortest path—we can always find a path with lower weight by following the
proposed “shortest” path and then traversing the negative-weight cycle. If there is a negative
weight cycle on some path from s to v, we define ( ) .
Figure 1 illustrates the effect of negative weights and negative-weight cycles on shortest-path
weights. Because there is only one path from s to a (the path 〈 〉), we have ( )
( ) . Similarly, there is only one path from s to b, and so ( ) ( )
( ) ( ) . There are infinitely many paths from s to c: 〈 〉 〈 〉
〈 〉 and so on. Because the cycle 〈 〉has weight ( ) , the
shortest path from s to c is 〈 〉, with weight ( ) ( ) . Similarly, the shortest
path from s to d is 〈 〉 with weight ( ) ( ) ( ) . Analogously,
there are infinitely many paths from s to e: 〈 〉 〈 〉 〈 〉 and so on.
Because the cycle 〈 〉 has weight ( ) , however, there is no shortest path
from s to e. By traversing the negative-weight cycle 〈 〉 arbitrarily many times, we can
find paths from s to e with arbitrarily large negative weights, and so ( ) . Similarly,
( ) Because g is reachable from f , we can also find paths with arbitrarily large
negative weights from s to g, and so ( ) . Vertices h, i, and j also form a negative-
weight cycle. They are not reachable from s, and so ( ) ( ) ( ) .
In some of the shortest path algorithm may consider negative weight edges and some may not
consider.
Bellman-Ford Algorithm
The Bellman-Ford algorithm solves the single-source shortest-paths problem in the general
case in which edge weights may be negative. Given a weighted, directed graph G = (V,E)
with source s and weight function , the Bellman-Ford algorithm returns a boolean
value indicating whether or not there is a negative-weight cycle that is reachable from the
source. If there is such a cycle, the algorithm indicates that no solution exists. If there is no
such cycle, the algorithm produces the shortest paths and their weights.
Output: Shortest distance to all vertices from src. If there is a negative weight cycle, then
shortest distances are not calculated, negative weight cycle is reported.
Step1: This step initializes distances from source to all vertices as infinite and distance to
source itself as 0. Create an array dist[] of size |V| with all values as infinite except dist[src]
where src is source vertex.
Step2: This step calculates shortest distances. Do following |V|-1 times where |V| is the
number of vertices in given graph.
Step3: This step reports if there is a negative weight cycle in graph. Do following for each
edge u-v. If dist[v] > dist[u] + weight of edge uv, then “Graph contains negative weight
cycle” The idea of step 3 is, step 2 guarantees shortest distances if graph doesn‟t contain
negative weight cycle. If we iterate through all edges one more time and get a shorter path for
any vertex, then there is a negative weight cycle.
Pseudocode:
function bellmanFord(G, S)
for each vertex V in G
distance[V] <- infinite
previous[V] <- NULL
distance[S] <- 0
for each vertex V in G
for each edge (U,V) in G
tempDistance <- distance[U] + edge_weight(U, V)
if tempDistance < distance[V]
distance[V] <- tempDistance
previous[V] <- U
-1
2 5
6 3
-2
5
1 3 1 7
5 -2
3
4 6
-1
Here we will assume 1 as the source vertex and will find the shortest path to all other
vertices. Like all other dynamic programming problem it will also search all possible
solutions and choose the best solution.
Here number of vertices are 7 so we have to check the weight of each edges for 6 [Link]
each searching we have to the relaxation of each edges i.e If an edge is formed using the
vertices u and v and dist[v] > dist[u] + weight of edge uv, then update dist[v] = dist[u] +
weight of edge uv
(1,2),(1,3),(1,4),(2,5),(3,2),(3,5),(4,3),(4,6),(5,7),(6,7)
Step 1:
1 2 3 4 5 6 7
0
Step 2: Update the weight of each node in the 1st round
1,2 1,3 1,4 2,5 3,2 3,5 4,3 4,6 5,7 6,7
1 0
2 6 3
3 5 3
4 5
5 5
6 4
7 8 7
1 2 3 4 5 6 7
0
1,2 1,3 1,4 2,5 3,2 3,5 4,3 4,6 5,7 6,7
1 0
2 3 1
3 3
4 5
5 2
6 4
7 5
1 2 3 4 5 6 7
0
1,2 1,3 1,4 2,5 3,2 3,5 4,3 4,6 5,7 6,7
1 0
2 1
3 3
4 5
5 0
6 4
7 3
Distance of each node after the 3rd round
1 2 3 4 5 6 7
0
1,2 1,3 1,4 2,5 3,2 3,5 4,3 4,6 5,7 6,7
1 0
2 1
3 3
4 5
5 0
6 4
7 3
1 2 3 4 5 6 7
0
We will stop the iteration either after 6th round or if there is no change in the distance of each
node from source node between two consecutive rounds. As there is no change in the
distances between source node to other nodes between in 3rd and 4th round we will stop in
this stage and it will be final output. Hence, final shortest distance of source node from other
nodes are
1 2 3 4 5 6 7
0
Time complexity of this algorithm O(| || |). If V= n and E = n, then time complexity
will be ( )
4
1 2
5
5 -10
4 3
3
In the above example number of vertices are 4 so we have to search each edges 3 times.
Edges are (3,2),(4,3),(1,4),(1,2),(2,4)
Step 1:
1 2 3 4
0
1 2 3 4
0
1 2 3 4
0 8
1 2 3 4
0 -2 6
But no vertices should be relaxed in this stage because we have already completed 3rd round.
Distances of all the node from source node should be stagnant in 3rd stage itself. It indicates
the presence of negative weight cycle.
In this way even after | | iterations if any changes occurred in the distance it indicates
the presence of negative cycle in the graph and hence, shortest distance cannot be calculated.
In this graph 2,4 and 3 form negative weight cycle with total weight -2, so we cannot
calculate shortest distance using Bellman-Ford algorithm.
In our last lecture we have discussed about Bellman-Ford Algorithm which is used to find
the shortest path from source to any other node even if negative cycle is present in the graph.
Now we will discuss an algorithm named as Dijkstra’s Algorithm which will find the
shortest path from source node to any other node in a graph only if weight of each edge is
positive i.e for each . It is basically a greedy method to find the
optimal solution.
Dijkstra's algorithm (or Dijkstra's Shortest Path First algorithm, (SPF)) is an algorithm for
finding the shortest paths between nodes in a graph, which may represent, for example, road
networks. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published
three years later.
In order to understand this algorithm we have understand the relaxation method which I have
already discussed in the previous lecture. But here still I am discussing in brief.
X Y
The relaxation rule states if d[X]+cost(X,Y) < d[Y] then replace d[Y] with (d[X]+
cost(X,Y)). Here cost(X,Y) is the weight of the edge connected between node X and node Y.
7
2 4
2 1
2
1 6
1
4
5
3
3 5
Step 1: In the above graph we will choose node 1 as source node. The node which is directly
connected with source node distance between that node and source node will be replaced by
weight of the edge connected between them. Foe all other nodes replace this distance with .
2 3 4 5 6
Cost 2 4
Step 2: Choose the node with least cost as the next node i.e node 2 in our case and marked it
as travelled. Update cost of each node using relaxation method.
2 3 4 5 6
Cost 2 3 9
Step 3: Node which is already travelled will not be travelled again. So our next node will be
3. Update the cost of the node 5 using relaxation technique (3 +3 < ) as it is directly
connected with 3
2 3 4 5 6
Cost 2 3 9 6
Step 4: Now we will go to node 5 and update the cost of node 4 and node 6
2 3 4 5 6
Cost 2 3 8 6 11
Step 5: As out of node 4 and 6 cost of node 4 is less we will travel to node 4 and update the
cost of node 6
2 3 4 5 6
Cost 2 3 8 6 9
Step 6: Only remaining vertex is 6 and from that vertex there is no path to go. Hence we will
stop here and final cost of each node is the shortest distance of each node from source node.
In order to calculate the time complexity of this algorithm we have to have to consider the
worst case scenario i.e from each node we need to search all other nodes (in case of fully
connected graph). So if there is n no. of vertices in a graph then worst case time complexity
will be O(n2).
3
1 2
5 -3
4 3
2
Now in the above graph we have to find the shortest path from source node 1 using Dijkstra’s
Algorithm. Here weight of one node is –ve as shown in the graph.
Step 2: Though the cost of node 2 is low but there is no outgoing path. So we will go to node
4. But we will mark node 2 as travelled node
2 3 4
Cost 3 7 5
Step 3: Start from node 3 and check the cost of node 2. But it is found that it is okay so leave
it to 3.
2 3 4
Cost 3 7 5
So Dijkstra’s Algorithm gives the correct result even if there is negative edge. Now if we
modify the weight of the negative edge in the same graph
3
1 2
5 -6
4 3
2
Here step 1 and step 2 will remain same as the previous case. Only step 3 will be changed as
follows
2 3 4
Cost 3 7 5
Now when check for relaxation of node 2 from node 3 it will updated to 1 which cannot be
possible because node 2 is already travelled.
Hence if negative edges present in the graph Dijkstra’s Algorithm may not work. So we
cannot use this algorithm to find shortest path in the graph with negative edges.
Pseudocode of Dijkstra’s Algorithm
Consider the graph shown below. We have to find the shortest path between every node in
the graph using Floyd-Warshall algorithm
8
1 2
3
2 7 2
5
4 3
1
First we will create a square matrix whose every entry is the weight of the edge connecting
between the nodes. The matrix is designated by A0 . The number written in red color in A0
indicates nodes in the graph. As there is no path from a particular node to that node itself, the
diagonal elements will be 0. If there is no direct connection from a node to another node entry
corresponding to that element will be . All other entry in the matrix will be the weight of
the edges connecting between the nodes
A0=
As we have four nodes or vertices in the graph we have to perform the following step four
times. During each step we have to calculate the distance of every of nodes through particular
nodes and update the entry of the matrix. As for example in step 1 we have to calculate
distance between each node or vertices through node 1
Step 1: Here we will calculate distance between each nodes or vertices through node 1. Here
we will keep the entry of first row and first column fixed and updated all other entries
according to the following rule:
If distance between two nodes through node 1 is less than the present value in the matrix
update it with that distance otherwise leave it.
A1=
Step 2: Here we will calculate distance between each nodes or vertices through node 2. Hence
we will keep the entry of A1 2nd row and 2nd column fixed and updated all other entries in the
similar way. After 2nd step content of the matrix will be
A2=
Step 3: Here we will calculate distance between each nodes or vertices through node 3. Hence
we will keep the entry of A2 3rd row and 3rd column fixed and updated all other entries in the
similar way. After 3rd step content of the matrix will be
A3=
Step 4: Here we will calculate distance between each nodes or vertices through node 4. Hence
we will keep the entry of A3 4th row and 4th column fixed and updated all other entries in the
similar way. After 4th step content of the matrix will be
A4=
Now each entry in A4 indicates the shortest distance between each pair of the nodes in the
graph. Like shortest distance between node 2 and node 3 is 2.
In general we can write [ ] { [ ] [ ] [ ]}
Output − Matrix to for shortest path between any vertex to any vertex.
Begin
for k := 0 to n, do
for i := 0 to n, do
for j := 0 to n, do
if cost[i,k] + cost[k,j] < cost[i,j], then
cost[i,j] := cost[i,k] + cost[k,j]
done
done
done
display the current cost matrix
End