1 - Design and Analysis of Algorithms by Karamagi, Robert
1 - Design and Analysis of Algorithms by Karamagi, Robert
1 - Design and Analysis of Algorithms by Karamagi, Robert
Algorithm
Asymptotic Analysis
Recurrence
Substitution Method
Iteration Method
Recursion Tree Method
Master Method
Sorting Analysis
Bubble Sort
Selection Sort
Insertion Sort
Divide and Conquer
Max - Min Problem
Binary Search
Merge Sort
Tower of Hanoi
Sorting
Heap Sort
Quick Sort
Stable Sorting
Lower Bound Theory
Sorting in Linear Time
Linear Time Sorting
Counting Sort
Bucket Sort
Radix Sort
Hashing
Hash Tables
Methods of Hashing
Open Addressing Techniques
Hash Function
Binary Search Trees
Red Black Tree
Dynamic Programming
Fibonacci Sequence
Matrix Chain Multiplication
Longest Common Sequence (LCS)
0/1 Knapsack Problem
Dutch National Flag
Longest Palindrome Subsequence
Greedy Algorithm
Activity Selection Problem
Fractional Knapsack
Huffman Codes
Activity or Task Scheduling Problem
Travelling Sales Person Problem
Backtracking
Recursive Maze Algorithm
Hamiltonian Circuit Problems
Subset-Sum Problem
N-Queens Problem
Minimum Spanning Tree
Kruskal's Algorithm
Prim's Algorithm
Shortest Paths
Negative Weight Edges
Representing Shortest Path
Relaxation
Dijkstra's Algorithm
Bellman-Ford Algorithm
Single Source Shortest Path in Directed Acyclic Graphs
All-Pairs Shortest Paths
Floyd-Warshall Algorithm
Johnson's Algorithm
Flow
Network Flow Problems
Ford-Fulkerson Algorithm
Maximum Bipartite Matching
Sorting Networks
Comparison Networks
Bitonic Sorting Network
Merging Network
Complexity Theory
Polynomial Time Verification
NP-Completeness
Circuit Satisfiability
3CNF Satisfiability
Clique
Vertex Cover
Subset Cover
Approximate Algorithms
Vertex Cover
Traveling-salesman Problem
String Matching
Naive String Matching Algorithm
Rabin-Karp-Algorithm
String Matching with Finite Automata
Knuth-Morris-Pratt (KMP)Algorithm
Boyer-Moore Algorithm
Algorithm
A finite set of instruction that specifies a sequence of operation is to be
carried out in order to solve a specific problem or class of problems is called
an Algorithm.
Why study Algorithm?
As the speed of processor increases, performance is frequently said to be less
central than other software quality characteristics (e.g. security, extensibility,
reusability etc.). However, large problem sizes are commonplace in the area
of computational science, which makes performance a very important factor.
This is because longer computation time, to name a few mean slower results,
less through research and higher cost of computation (if buying CPU Hours
from an external party). The study of Algorithm, therefore, gives us a
language to express performance as a function of problem size.
Algorithm
An algorithm can be defined as a well-defined computational procedure that
takes some values, or the set of values, as an input and produces some value,
or the set of values, as an output. An algorithm is thus a sequence of
computational steps that transform the input into output.
It describes specific computational procedures for achieving the input-output
relationship.
For Example, We need to sort the sequence of number into ascending order.
Here is how we define the sorting problem.
Input:
A sequence of n number (a1, a2,......an)
Output:
A permutation (reordering) (a'1, a'2 .... a'n) of the input sequence such that (a'1
≤ a'2 ≤ ....≤ a'n)
An algorithm must have the following properties:
Need of Algorithm
1. To understand the basic idea of the problem.
2. To find an approach to solve the problem.
3. To improve the efficiency of existing techniques.
4. To understand the basic principles of designing the algorithms.
5. To compare the performance of the algorithm with respect to other
techniques.
6. It is the best method of description without describing the implementation
detail.
7. The Algorithm gives a clear description of requirements and goal of the
problem to the designer.
8. A good design can produce a good solution.
9. To understand the flow of the problem.
10. To measure the behavior (or performance) of the methods in all cases
(best cases, worst cases, average cases)
11. With the help of an algorithm, we can also identify the resources
(memory, input-output) cycles required by the algorithm.
12. With the help of algorithm, we convert art into a science.
13. To understand the principle of designing.
14. We can measure and analyze the complexity (time and space) of the
problems concerning input size without implementing and running it; it will
reduce the cost of design.
Algorithm vs Program
A finite set of instructions that specifies a sequence of operations to be
carried out to solve a specific problem of a class of problem is called an
algorithm.
On the other hand, the Program doesn't have to satisfy the finiteness
condition. For example, we can think of an operating system that continues in
a "wait" loop until more jobs are entered. Such a program doesn't terminate
unless the system crashes.
Given a Problem to solve, the design Phase produces an algorithm, and the
implementation phase then generates a program that expresses the designed
algorithm. So, the concrete expression of an algorithm in a particular
programming language is called a program.
Complexity of Algorithm
It is very convenient to classify algorithm based on the relative amount of
time or relative amount of space they required and specify the growth of
time/space requirement as a function of input size.
Time Complexity: Running time of a program as a function of the size of the
input.
Space Complexity: Some forms of analysis could be done based on how
much space an algorithm needs to complete its task. This space complexity
analysis was critical in the early days of computing when storage space on
the computer was limited. When considering this algorithm are divided into
those that need extra space to do their work and those that work in place.
But now a day's problem of space rarely occurs because space on the
computer (internal or external) is enough.
Broadly, we achieve the following types of analysis -
Asymptotic Analysis
Resources for an algorithm are usually expressed as a function regarding
input. Often this function is messy and complicated to work. To study
Function growth efficiently, we reduce the function down to the important
part.
Let f (n) = an2+bn+c
In this function, the n2 term dominates the function that is when n gets
sufficiently large.
Dominate terms are what we are interested in reducing a function, in this; we
ignore all constants and coefficient and look at the highest order term
concerning n.
Asymptotic Notation
The word Asymptotic means approaching a value or curve arbitrarily closely
(i.e., as some sort of limit is taken).
Asymptotic Analysis
It is a technique of representing limiting behavior. The methodology has the
applications across science. It can be used to analyze the performance of an
algorithm for some large data set.
In computer science in the analysis of algorithms, considering the
performance of algorithms when applied to very large input datasets.
The simplest example is a function ƒ (n) = n2+3n, the term 3n becomes
insignificant compared to n2 when n is very large. The function "ƒ (n) is said
to be asymptotically equivalent to n2 as n → ∞", and here is written
symbolically as ƒ (n) ~ n2.
Asymptotic notations are used to write fastest and slowest possible running
time for an algorithm. These are also referred to as 'best case' and 'worst case'
scenarios respectively.
"In asymptotic notations, we derive the complexity concerning the size of the
input. (Example in terms of n)"
"These notations are important because without expanding the cost of
running the algorithm, we can estimate the complexity of the algorithms."
Why is Asymptotic Notation Important?
1. They give simple characteristics of an algorithm's efficiency.
2. They allow the comparisons of the performances of various algorithms.
Asymptotic Notations
Asymptotic Notation is a way of comparing function that ignores constant
factors and small input sizes. Three notations are used to calculate the
running time complexity of an algorithm:
1. Big-oh notation: Big-oh is the formal method of expressing the upper
bound of an algorithm's running time. It is the measure of the longest amount
of time. The function f (n) = O (g(n)) [read as "f of n is big-oh of g of n"] if
and only if exist positive constant c and such that
f(n) ⩽ k.g (n)f(n) ⩽ k.g(n) for n>n0n>n0 in all case
Hence, function g (n) is an upper bound for function f (n), as g (n) grows
faster than f (n)
Example
1. 3n+2=O(n) as 3n+2≤4n for all n≥2
2. 3n+3=O(n) as 3n+3≤4n for all n≥3
Hence, the complexity of f(n) can be represented as O(g(n))
2. Omega() Notation: The function f(n) = Ω (g(n)) [read as "f of n is omega
of g of n"] if and only if there exists positive constant c and n0 such that F(n)
≥ k* g (n) for all n, n≥ n0
Example
F(n) =8n2+2n-3≥8n2-3
=7n2+(n2-3)≥7n2 (g(n))
Thus, k1=7
Hence, the complexity of f (n) can be represented as Ω (g (n))
3. Theta (θ): The function f (n) = θ (g (n)) [read as "f is the theta of g of n"] if
and only if there exists positive constant k1, k2 and k0 such that
1. Sequencing
2. If-then-else
3. for loop
4. While loop
1. Sequencing
Suppose our algorithm consists of two parts A and B. A takes time tA and B
takes time tB for computation. The total computation "tA + tB" is according to
the sequence rule. According to maximum rule, this computation time is
(max (tA,tB)).
Example
Suppose tA =O (n) and tB = θ (n2).
Then, the total computation time can be calculated as
Computation Time = tA + tB
= (max (tA,tB)
= (max (O (n), θ (n2)) = θ (n2)
2. If-then-else
The total time computation is according to the condition rule-"if-then-else."
According to the maximum rule, this computation time is max (tA,tB).
Example
Suppose tA = O (n2) and tB = θ (n2)
Calculate the total computation time for the following:
Takes
If the algorithms consist of nested "for" loops, then the total computation
time is
For i ← 1 to n
{
For j ← 1 to n
{
P (ij)
}
}
Example
Consider the following "for" loop, Calculate the total computation time for
the following:
For i ← 2 to n-1
{
For j ← 3 to i
{
Sum ← Sum+A [i] [j]
}
}
Solution
The total Computation time is:
4. While loop
The Simple technique for analyzing the loop is to determine the function of
variable involved whose value decreases each time around. Secondly, for
terminating the loop, it is necessary that value must be a positive integer. By
keeping track of how many times the value of function decreases, one can
obtain the number of repetition of the loop. The other approach for analyzing
"while" loop is to treat them as recursive algorithms.
Algorithm
1. [Initialize] Set k: =1, LOC: =1 and MAX: = DATA [1]
2. Repeat steps 3 and 4 while K≤N
3. if MAX<DATA [k],then:
Set LOC: = K and MAX: = DATA [k]
4. Set k: = k+1
[End of step 2 loop]
5. Write: LOC, MAX
6. EXIT
Example
The running time of algorithm array Max of computing the maximum
element in an array of n integer is O (n).
Solution
array Max (A, n)
1. Current max ← A [0]
2. For i ← 1 to n-1
3. do if current max < A [i]
4. then current max ← A [i]
5. return current max.
The number of primitive operation t (n) executed by this algorithm is at least.
2 + 1 + n +4 (n-1) + 1=5n
2 + 1 + n + 6 (n-1) + 1=7n-2
The best case T(n) =5n occurs when A [0] is the maximum element. The
worst case T(n) = 7n-2 occurs when element are sorted in increasing order.
We may, therefore, apply the big-Oh definition with c=7 and n0=1 and
conclude the running time of this is O (n).
Recurrence
Recurrence
A recurrence is an equation or inequality that describes a function in terms of
its values on smaller inputs. To solve a Recurrence Relation means to obtain
a function defined on the natural numbers that satisfy the recurrence.
For Example, the Worst Case Running Time T(n) of the MERGE SORT
Procedures is described by the recurrence.
T (n) = θ (1) if n=1
2T + θ (n) if n>1
There are four methods for solving Recurrence:
1. Substitution Method
2. Iteration Method
3. Recursion Tree Method
4. Master Method
Substitution Method
The Substitution Method Consists of two main steps:
Example
Solve the equation by Substitution Method.
T (n) = T +n
We have to show that it is asymptotically bound by O (log n).
Solution
For T (n) = O (log n)
We have to show that for some constant c
T (n) ≤c logn.
Put this in given Recurrence Equation.
T (n) ≤c log +1
T (n) = 2T + n n>1
Find an Asymptotic bound on T.
Solution
We guess the solution is O (n (logn)).Thus for constant 'c'.
T (n) ≤c n logn
Put this in given Recurrence Equation.
Now,
Iteration Method
It means to expand the recurrence and express it as a summation of terms of n
and initial condition.
Example
Consider the Recurrence
T (n) = 1 if n=1
= 2T (n-1) if n>1
Solution
T (n) = 2T (n-1)
= 2[2T (n-2)] = 22T (n-2)
= 4[2T (n-3)] = 23T (n-3)
= 8[2T (n-4)] = 24T (n-4) (Eq.1)
Repeat the procedure for i times
T (n) = 2i T (n-i)
Put n-i=1 or i= n-1 in (Eq.1)
T (n) = 2n-1 T (1)
= 2n-1 .1 {T (1) =1 .....given}
= 2n-1
Example
Consider the Recurrence
T (n) = T (n-1) +1 and T (1) = θ (1).
Solution
T (n) = T (n-1) +1
= (T (n-2) +1) +1 = (T (n-3) +1) +1+1
= T (n-4) +4 = T (n-5) +1+4
= T (n-5) +5= T (n-k) + k
Where k = n-1
T (n-k) = T (1) = θ (1)
T (n) = θ (1) + (n-1) = 1+n-1=n= θ (n).
Consider T (n) = 2T + n2
We have to obtain the asymptotic bound using recursion tree method.
Solution
The Recursion tree for the above recurrence is
Example
Consider the following recurrence
T (n) = 4T +n
Obtain the asymptotic bound using recursion tree method.
Solution
The recursion trees for the above recurrence
Example
Consider the following recurrence
Obtain the asymptotic bound using recursion tree method.
Solution
The given Recurrence has the following recursion tree
When we add the values across the levels of the recursion trees, we get a
value of n for every level. The longest path from the root to leaf is
Master Method
The Master Method is used for solving the following types of recurrence
T (n) = a T + f (n) with a≥1 and b≥1 be constant & f(n) be a function and
can be interpreted as
Let T (n) is defined on non-negative integers by the recurrence.
T (n) = a T + f (n)
In the function to the analysis of a recursive algorithm, the constants and
function take on the following significance:
Master Theorem
It is possible to complete an asymptotic tight bound in these three cases:
Case1
T (n) = a T
a = 8, b=2, f (n) = 1000 n2, logba = log28 = 3
Case 2
If it is true, for some constant k ≥ 0 that:
F (n) = Θ
then it follows that: T (n) = Θ
Example
Therefore: T (n) = Θ
= Θ (n log n)
Case 3
If it is true f(n) = Ω for some constant ε >0 and it also true that: a
T (n) = 2
Solution
2
If we will choose c =1/2, it is true:
∀ n ≥1
So it follows: T (n) = Θ ((f (n))
T (n) = Θ(n2)
Sorting Analysis
Bubble Sort
Bubble Sort also known as Exchange Sort, is a simple sorting algorithm. It
works by repeatedly stepping throughout the list to be sorted, comparing two
items at a time and swapping them if they are in the wrong order. The pass
through the list is duplicated until no swaps are desired, which means the list
is sorted.
This is the easiest method among all sorting algorithms.
BUBBLE SORT (A)
1. for i ← 1 to length [A]
2. for k ← length [A] down to i+1
3. if A[k] <A[k-1]
4. exchange (A[k], A [k-1])
Analysis
Input: n elements are given.
Output: the number of comparisons required to make sorting.
Logic: If we have n elements in bubble sort, then n-1 passes are required to
find a sorted array.
In pass 1: n-1 comparisons are required
In pass 2: n-2 comparisons are required
In pass 3: n-3 comparisons are required
............................................................................
...............................................................................
In pass n-1: 1 comparisons is required
Total comparisons: T (n) = (n-1) + (n-2) +...........+ 1
=
= o (n2)
Therefore complexity is of order n2
Example
Unsorted List: A = {7, 2, 1, 4, 5, 9, 6}
i.e., A [] =
7 2 1 4 3 6 9
Here length [A] =7
i=1 to 7 and j=7 to 2
i=1, j=7
A [7] =6 and A [6] =9. So A [7] <A [6]
Now Exchange (A [7], A [6])
Now, i=1, j=6 then A [6] =6
A [5] = 3 and A [5] < A [6]
Now, i=1, j=5 then A [5] = 3
A [4] = 4 and A [5] < A [4]
So, exchange (A [5], A [4])
And A [] =
7 2 1 3 4 6 9
Selection Sort
The selection sort enhances the bubble sort by making only a single swap for
each pass through the rundown. Indorder to do this, a selection sort searches
for the biggest value as it makes a pass and, after finishing the pass, places it
in the best possible area. Similarly as with a bubble sort, after the first pass,
the biggest item is in the right place. After the second pass, the following
biggest is set up. This procedure proceeds and requires n-1 goes to sort n
item, since the last item must be set up after the (n-1) st pass.
Algorithm
SELECTION SORT (A)
1. k ← length [A]
2. for j ←1 to n-1
3. smallest ← j
4. for I ← j + 1 to k
5. if A [i] < A [ smallest]
6. then smallest ← i
7. exchange (A [j], A [smallest])
Analysis
=
= o (n2)
Therefore complexity is of order n2
Example
Sort the following array using selection sort: A [] = (7, 4, 3, 6, 5).
A [] =
7 4 3 6 5
1 Iteration:
Smallest =7
4 < 7, smallest = 4
3 < 4, smallest = 3
6 > 3, smallest = 3
5 > 3, smallest = 3
Swap 7 and 3
3 4 7 6 5
2nd iteration:
Smallest = 4
4 < 7, smallest = 4
4 < 6, smallest = 4
4 < 5, smallest = 4
No Swap
3 4 7 6 5
3rd iteration:
Smallest = 7
6 < 7, smallest = 6
5 < 7, smallest = 5
Swap 7 and 5
3 4 5 6 7
4th iteration:
Smallest = 6
6< 7, smallest = 6
No Swap
3 4 5 6 7
Insertion Sort
It is a very simple method to sort the number in an increasing or decreasing
order.
It has various advantages:
1. It is simple to implement.
2. It is efficient on small datasets.
3. It is stable (does not change the relative order of elements with
equal keys)
4. It is in-place (only requires a constant amount O (1) of extra
memory space).
5. It is an online algorithm, in that it can sort a list as it receives it.
Algorithm
INSERTION SORT (A)
1. For k ← 2 to length [A]
2. Do key ← A[k]
3. i=k-1
4. while i>0 and A[i]>key
5. do A[i+1] ← A[i]
6. i=i-1
7. A[i+1] ← key
Analysis
Input: n elements are given.
Output: the number of comparisons required to make sorting.
Logic: If we have n elements in insertion sort, then n-1 passes are required to
find a sorted array.
In pass 1: no comparison is required
In pass 2: 1 comparison is required
In pass 3: 2 comparisons are required
............................................................................
...............................................................................
In pass n: n-1 comparisons are required
Total comparisons: T (n) = 1+2+3+...........+ n-1
=
= o (n2)
Therefore complexity is of order n2
Example
Illustrate the operation of INSERTION SORT on the array A = (4, 15, 7, 18,
and 16).
Solution
A [] =
4 15 7 18 16
For j=2 to 5
J=2, key=A [2]
Key=15
I=2-1=1, i=1
While i>0 and A [1]>15
A Condition false, so no change
Now=3, key=A [3] =7
I=3-1=2
I=2, key=7
While i>0 and A [2]>key
Condition is true
So A [2+1] ← A [2]
A [3] ← A [2]
i.e.
4 15 18 16
Examples
The specific computer algorithms are based on the Divide & Conquer
approach:
1. Relational Formula
2. Stopping Condition
T (n) = 2 T → Eq (i)
T (2) = 1, time required to compare two elements/items. (Time is measured in
units of the number of comparisons)
→ Eq (ii)
Put eq (ii) in eq (i)
algorithm on n elements/items =
Number of comparisons requires applying general approach on n elements =
(n-1) + (n-1) = 2n-2
From this example, we can analyze, that how to reduce the number of
comparisons by using this technique.
Analysis
Suppose we have the array of size 8 elements.
Method 1: requires (2n-2), (2x8)-2=14 comparisons
Method 2: requires
It is evident; we can reduce the number of comparisons (complexity) by
using a proper technique.
Binary Search
1. In Binary Search technique, we search an element in a sorted array by
recursively dividing the interval in half.
2. Firstly, we take the whole array as an interval.
3. If the Pivot Element (the item to be searched) is less than the item in the
middle of the interval, We discard the second half of the list and recursively
repeat the process for the first half of the list by calculating the new middle
and last element.
4. If the Pivot Element (the item to be searched) is greater than the item in the
middle of the interval, we discard the first half of the list and work
recursively on the second half by calculating the new beginning and middle
element.
5. Repeatedly, check until the value is found or interval is empty.
Analysis
Find mid =
Compare the search item with the mid item.
Case 1: item = A[mid], then LOC = mid, but it the best case and T (n) = 1
Case 2: item ≠A [mid], then we will split the array into two equal parts of
size .
And again find the midpoint of the half-sorted array and compare with search
element.
Repeat the same process until a search element is found.
At least there will be only one term left that's why that term will compare out,
and only one comparison be done that's why
1. Divide: Divide the unsorted list into two sublists of about half
the size.
2. Conquer: Sort each of the two sublists recursively until we have
list sizes of length 1, in which case the list items are returned.
3. Combine: Join the two sorted Sub lists back into one sorted list.
The Main purpose is to sort the unsorted list in nondecreasing order.
Algorithm-Merge Sort
1. If p<r
2. Then q ← ( p+ r)/2
3. MERGE-SORT (A, p, q)
4. MERGE-SORT ( A, q+1,r)
5. MERGE ( A, p, q ,r)
The following figure illustrates the dividing (splitting) procedure.
In this method, we split the given list into two halves. Then recursively
analyzing merge sort and dividing. We get many sorted lists.
At last, we combine the sorted lists.
Analysis of Merge Sort
Let T (n) be the total time taken in Merge Sort
So T (n) = 2T + n.........equation 1
Note: Stopping Condition T (1) =0 because at last there will be only 1
element left which need to be copied and there will be no comparison.
=i
log2n=i
From 6 equation
Tower of Hanoi
1. It is a classic problem where you try to move all the disks from one peg to
another peg using only three pegs.
2. Initially, all of the disks are stacked on top of each other with larger disks
under the smaller disks.
3. You may move the disks to any of three pegs as you attempt to relocate all
of the disks, but you cannot place the larger disks over smaller disks and only
one disk can be transferred at a time.
This problem can be easily solved by Divide & Conquer algorithm
In the above 7 step all the disks from peg A will be transferred to C given
Condition
1. Only one disk will be shifted at a time.
2. Smaller disk can be placed on larger disk.
Let T (n) be the total time taken to move n disks from peg A to peg C
Moving n-1 disks from the first peg to the second peg. This can be done in T
(n-1) steps.
1. Moving larger disks from the first peg to the third peg will
require first one step.
2. Recursively moving n-1 disks from the second peg to the third
peg will require again T (n-1) step.
The root of tree A [1] and gives index 'i' of a node that indices of its parents,
left child, and the right child can be computed.
PARENT (i)
Return floor (i/2)
LEFT (i)
Return 2i
RIGHT (i)
Return 2i+1
Heapify Method
1. Maintaining the Heap Property: Heapify is a procedure for manipulating
heap Data Structure. It is given an array A and index I into the array. The
subtree rooted at the children of A [i] are heap but node A [i] itself may
probably violate the heap property i.e. A [i] < A [2i] or A [2i+1]. The
procedure 'Heapify' manipulates the tree rooted as A [i] so it becomes a heap.
MAX-HEAPIFY (A, i)
1. l ← left [i]
2. r ← right [i]
3. if l≤ heap-size [A] and A[l] > A [i]
4. then largest ← l
5. Else largest ← i
6. If r≤ heap-size [A] and A [r] > A[largest]
7. Then largest ← r
8. If largest ≠ i
9. Then exchange A [i] A [largest]
10. MAX-HEAPIFY (A, largest)
Analysis
The maximum levels an element could move up are Θ (log n) levels. At each
level, we do simple comparison which O (1). The total time for heapify is
thus O (log n).
Building a Heap
BUILDHEAP (array A, int n)
1 for i ← n/2 down to 1
2 do
3 HEAPIFY (A, i, n)
Heap-Sort Algorithm
HEAP-SORT (A)
1. BUILD-MAX-HEAP (A)
2. For I ← length[A] down to Z
3. Do exchange A [1] ←→ A [i]
4. Heap-size [A] ← heap-size [A]-1
5. MAX-HEAPIFY (A,1)
Analysis: Build max-heap takes O (n) running time. The Heap Sort algorithm
makes a call to 'Build Max-Heap' which we take O (n) time & each of the (n-
1) calls to Max-heap to fix up a new heap. We know 'Max-Heapify' takes
time O (log n)
The total running time of Heap-Sort is O (n log n).
Example
Illustrate the Operation of BUILD-MAX-HEAP on the array.
A = (5, 3, 17, 10, 84, 19, 6, 22, 9)
Solution
Originally:
Heap-Size (A) =9, so first we call MAX-HEAPIFY (A, 4)
And I = 4.5= 4 to 1
In this figure, that max-heap with a node whose index is 'i' heavily shaded
The max-heap after one more iteration of the while loops, the A [PARENT (i)
≥A (i)] the max-heap property now holds and the procedure terminates.
Heap-Delete
Heap-DELETE (A, i) is the procedure, which deletes the item in node 'i' from
heap A, HEAP-DELETE runs in O (log n) time for n-element max heap.
HEAP-DELETE (A, i)
1. A [i] ← A [heap-size [A]]
2. Heap-size [A] ← heap-size [A]-1
3. MAX-HEAPIFY (A, i)
Quick Sort
It is an algorithm of Divide & Conquer type.
Divide: Rearrange the elements and split arrays into two sub-
arrays and an element in between search that each element in
left sub array is less than or equal to the average element and
each element in the right sub- array is larger than the middle
element.
Conquer: Recursively, sort two sub arrays.
Combine: Combine the already sorted array.
Algorithm
QUICKSORT (array A, int m, int n)
1 if (n > m)
2 then
3 i ← a random index from [m,n]
4 swap A [i] with A[m]
5 o ← PARTITION (A, m, n)
6 QUICKSORT (A, m, o - 1)
7 QUICKSORT (A, o + 1, n)
Partition Algorithm
Partition algorithm rearranges the sub arrays in a place.
PARTITION (array A, int m, int n)
1 x ← A[m]
2o←m
3 for p ← m + 1 to n
4 do if (A[p] < x)
5 then o ← o + 1
6 swap A[o] with A[p]
7 swap A[m] with A[o]
8 return o
Example of Quick Sort
44 33 11 55 77 90 40 60 99 22 88
Let 44 be the Pivot element and scanning done from right to left
Comparing 44 to the right-side elements, and if right-side elements are
smaller than 44, then swap it. As 22 is smaller than 44 so swap them.
22 33 11 55 77 90 40 60 99 44 88
Now comparing 44 to the left side element and the element must
be greater than 44 then swap them. As 55 are greater than 44 so swap them.
22 33 11 44 77 90 40 60 99 55 88
Recursively, repeating steps 1 & steps 2 until we get two lists one left from
pivot element 44 & one right from pivot element.
22 33 11 40 77 90 44 60 99 55 88
Swap with 77:
22 33 11 40 44 90 77 60 99 55 88
Now, the element on the right side and left side are greater than and smaller
than 44 respectively.
Now we get two sorted lists:
And these sublists are sorted under the same process as above done.
These two sorted sublists side by side.
Merging Sublists:
SORTED LISTS
Worst Case Analysis
It is the case when items are already in sorted form and we try to sort them
again. This will takes lots of time and space.
Equation
T (n) =T(1)+T(n-1)+n
T (1) is time taken by pivot element.
T (n-1) is time taken by remaining element except for pivot element.
N: the number of comparisons required to identify the exact position of itself
(every element)
If we compare first element pivot with other, then there will be 5
comparisons.
It means there will be n comparisons if there are n items.
Pivot element will do n comparison and we are doing average case so,
= n+1 + x2 (T(0)+T(1)+T(2)+...T(n-2)+T(n-1))
Put n=n-1 in eq 3
Put 4 eq in 3 eq
Put n=n-2 in eq 3
Put 6 eq in 5 eq
Put n=n-3 in eq 3
Put 8 eq in 7 eq
From 10 eq
Multiply and divide the last term by 2
Stable Sorting
A sorting algorithm is said to be stable if two objects with equal keys appear
in the same order in sorted output as they appear in the input unsorted array.
Some Sorting Algorithm is stable by nature like Insertion Sort, Merge Sort
and Bubble Sort etc.
Sorting Algorithm is not stable like Quick Sort, Heap Sort etc.
Another Definition of Stable Sorting
A Stable Sort is one which preserves the original order of input set, where the
comparison algorithm does not distinguish between two or more items. A
Stable Sort will guarantee that the original order of data having the same rank
is preserved in the output.
In Place Sorting Algorithm:
1. An In-Place Sorting Algorithm directly modifies the list that is
received as input instead of creating a new list that is then
modified.
2. In this Sorting, a small amount of extra space it uses to
manipulate the input set. In other Words, the output is placed in
the correct position while the algorithm is still executing, which
means that the input will be overwritten by the desired output
on run-time.
3. In-Place, Sorting Algorithm updates input only through
replacement or swapping of elements.
4. An algorithm which is not in-place is sometimes called not-in-
Place or out of Place.
5. An Algorithm can only have a constant amount of extra space,
counting everything including function call and Pointers,
Usually; this space is O (log n).
Note
1. Comparisons Trees
2. Oracle and adversary argument
3. State Space Method
1. Comparison Trees
In a comparison sort, we use only comparisons between elements to gain
order information about an input sequence (a1; a2......an).
Given ai,aj from (a1, a2.....an). We Perform One of the Comparisons
To determine their relative order, if we assume all elements are distinct, then
we just need to consider ai ≤ aj '=' is excluded &, ≥,≤,>,< are equivalent.
Consider sorting three numbers a1, a2, and a3. There are 3! = 6 possible
combinations:
(a1, a2, a3), (a1, a3, a2),
(a2, a1, a3), (a2, a3, a1)
(a3, a1, a2), (a3, a2, a1)
The Comparison based algorithm defines a decision tree.
Decision Tree: A decision tree is a full binary tree that shows the
comparisons between elements that are executed by an appropriate sorting
algorithm operating on an input of a given size. Control, data movement, and
all other conditions of the algorithm are ignored.
In a decision tree, there will be an array of length n.
So, total leaves will be n! (I.e. total number of comparisons)
If tree height is h, then surely
n! ≤2n (tree will be binary)
Taking an Example of comparing a1, a2, and a3.
Left subtree will be true condition i.e. ai ≤ aj
Right subtree will be false condition i.e. ai >aj
k >=
k >=log2(n+1)
Step 6:
T (n) = k
Step7:
T (n) >=log2(n+1)
Here, the minimum number of Comparisons to perform a task of the search
of n terms using Binary Search
2. Oracle and adversary argument
Another technique for obtaining lower bounds consists of making use of an
"oracle."
Given some model of estimation such as comparison trees, the oracle tells us
the outcome of each comparison.
In order to derive a good lower bound, the oracle efforts it's finest to cause
the algorithm to work as hard as it might.
It does this by deciding as the outcome of the next analysis, the result which
matters the most work to be needed to determine the final answer.
And by keeping step of the work that is finished, a worst-case lower bound
for the problem can be derived.
Example
(Merging Problem) given the sets A (1: m) and B (1: n), where the
information in A and in B are sorted. Consider lower bounds for algorithms
combining these two sets to give an individual sorted set.
Consider that all of the m+n elements are specific and A (1) < A (2) < ....< A
(m) and B (1) < B (2) < ....< B (n).
Elementary combinatory tells us that there are C ((m+n), n)) ways that the A's
and B's may merge together while still preserving the ordering within A and
B.
Thus, if we need comparison trees as our model for combining algorithms,
then there will be C ((m+n), n)) external nodes and therefore at least log C
((m+n), m) comparisons are needed by any comparison-based merging
algorithm.
If we let MERGE (m, n) be the minimum number of comparisons used to
merge m items with n items then we have the inequality
Log C ((m+n), m) MERGE (m, n) m+n-1.
The upper bound and lower bound can get promptly far apart as m gets much
smaller than n.
3. State Space Method
1. State Space Method is a set of rules that show the possible states (n-tuples)
that an algorithm can assume from a given state of a single comparison.
2. Once the state transitions are given, it is possible to derive lower bounds
by arguing that the finished state cannot be reached using any fewer
transitions.
3. Given n distinct items, find winner and loser.
4. Aim: When state changed count it that is the aim of State Space Method.
5. In this approach, we will count the number of comparison by counting the
number of changes in state.
6. Analysis of the problem to find out the smallest and biggest items by using
the state space method.
7. State: It is a collection of attributes.
8. Through this we sort out two types of Problems:
Find the largest & smallest element from an array of elements.
To find out largest & second largest elements from an array of an element.
9. For the largest item, we need 7 comparisons and what will be the second
largest item?
Now we count those teams who lose the match with team A
Teams are: B, D, and E
So the total no of comparisons are: 7
Let n is the total number of items, then
Comparisons = n-1 (to find the biggest item)
No of Comparisons to find out the 2nd biggest item = log2n-1
Lower bound (L (n)) is a property of the particular issue i.e. the sorting
problem, matrix multiplication not of any particular algorithm solving that
problem.
Lower bound theory says that no calculation can carry out the activity in less
than that of (L (n)) times the units for arbitrary inputs i.e. that for every
comparison based sorting algorithm must take at least L (n) time in the worst
case.
L (n) is the base overall conceivable calculation which is greatest finished.
Trivial lower bounds are utilized to yield the bound best alternative is to
count the number of elements in the problems input that must be prepared
and the number of output items that need to be produced.
The lower bound theory is the method that has been utilized to establish the
given algorithm in the most efficient way which is possible. This is done by
discovering a function g (n) that is a lower bound on the time that any
algorithm must take to solve the given problem. Now if we have an algorithm
whose computing time is the same order as g (n) , then we know that
asymptotically we cannot do better.
If f(n) is the time for some algorithm, then we write f (n) = Ω (g (n)) to mean
that g (n) is the lower bound of f(n) . This equation can be formally written, if
there exists positive constants c and n0 such that |f (n)| >= c|g (n)| for all n >
n0. In addition for developing lower bounds within the constant factor, we
are more conscious of the fact to determine more exact bounds whenever this
is possible.
Deriving good lower bounds is more challenging than arrange efficient
algorithms. This happens because a lower bound states a fact about all
possible algorithms for solving a problem. Generally, we cannot enumerate
and analyze all these algorithms, so lower bound proofs are often hard to
obtain.
Initialize C to zero
For each j from 1 to n increment C [A[j]] by 1
A [1] = 7 Processed
J=2, C [1, k] =
A [2] = 1 Processed
J=3, C [1, k]
A [3] = 3 Processed
J=4, C [1, k]
A [4] = 1 Processed
J=5, C [1, k]
A [5] = 2 Processed
UPDATED C is:
1. B[C[A[j] ← A [j]
2. C[A[j] ← C[A[j]-1
Bucket Sort
Bucket Sort runs in linear time on average. Like Counting Sort, bucket Sort is
fast because it considers something about the input. Bucket Sort considers
that the input is generated by a random process that distributes elements
uniformly over the intervalμ=[0,1].
To sort n input numbers, Bucket Sort
Radix Sort
Radix Sort is a Sorting algorithm that is useful when there is a constant'd'
such that all keys are d digit numbers. To execute Radix Sort, for p =1
towards 'd' sort the numbers with respect to the Pth digits from the right using
any linear time stable sort.
The Code for Radix Sort is straightforward. The following procedure
assumes that each element in the n-element array A has d digits, where digit 1
is the lowest order digit and digit d is the highest-order digit.
Here is the algorithm that sorts A [1.n] where each number is d digits long.
RADIX-SORT (array A, int n, int d)
1 for i ← 1 to d
2 do stably sort A to sort array A on digit i
Example
The first Column is the input. The remaining Column shows the list after
successive sorts on increasingly significant digit position. The vertical arrows
indicate the digits position sorted on to produce each list from the previous
one.
576 49[4] 9[5]4 [1]76 176
494 19[4] 5[7]6 [1]94 194
194 95[4] 1[7]6 [2]78 278
296 → 57[6] → 2[7]8 → [2]96 → 296
278 29[6] 4[9]4 [4]94 494
176 17[6] 1[9]4 [5]76 576
954 27[8] 2[9]6 [9]54 954
Hashing
Hashing is the transformation of a string of character into a usually shorter
fixed-length value or key that represents the original string.
Hashing is used to index and retrieve items in a database because it is faster
to find the item using the shortest hashed key than to find it using the original
value. It is also used in many encryption algorithms.
A hash code is generated by using a key, which is a unique value.
Hashing is a technique in which given key field value is converted into the
address of storage location of the record by applying the same operation on it.
The advantage of hashing is that allows the execution time of basic operation
to remain constant even for the larger side.
Why we need Hashing?
Suppose we have 50 employees, and we have to give 4 digit key to each
employee (as for security), and we want after entering a key, direct user map
to a particular position where data is stored.
If we give the location number according to 4 digits, we will have to reserve
0000 to 9999 addresses because anybody can use anyone as a key. There is a
lot of wastage.
In order to solve this problem, we use hashing which will produce a smaller
value of the index of the hash table corresponding to the key of the user.
Universal Hashing
Let H be a finite collection of hash functions that map a given universe U of
keys into the range {0, 1..... m-1}. Such a collection is said to be universal if
for each pair of distinct keys k,l ∈ U, the number of hash functions h ∈ H
for which h(k)= h(l) is at most |H|/m. In other words, with a hash function
randomly chosen from H, the chance of a collision between distinct keys k
and l is no more than the chance 1/m of a collision if h(k) and h(l)were
randomly and independently chosen from the set {0,1,...m-1}.
Rehashing
If any stage the hash table becomes nearly full, the running time for the
operations of will start taking too much time, insert operation may fail in
such situation, the best possible solution is as follows:
Example
Consider inserting the keys 10, 22, 31,4,15,28,17,88 and 59 into a hash table
of length m = 11 using open addressing with the primary hash function h' (k)
= k mod m .Illustrate the result of inserting these keys using linear probing,
using quadratic probing with c1=1 and c2=3, and using double hashing with
h2(k) = 1 + (k mod (m-1)).
Solution
Using Linear Probing the final state of hash table would be:
Using Quadratic Probing with c1=1, c2=3, the final state of hash table would
be h (k, i) = (h' (k) +c1*i+ c2 *i2) mod m where m=11 and h' (k) = k mod m.
Using Double Hashing, the final state of the hash table would be:
Hash Tables
It is a collection of items which are stored in such a way as to make it easy to
find them later.
Each position in the hash table is called slot, can hold an item and is named
by an integer value starting at 0.
The mapping between an item and a slot where the item belongs in a hash
table is called a Hash Function. A hash Function accepts a key and returns its
hash coding, or hash value.
Assume we have a set of integers 54, 26, 93, 17, 77, 31. Our first hash
function required to be as "remainder method" simply takes the item and
divide it by table size, returning remainder as its hash value i.e.
h item = item % (size of table)
Let us say the size of table = 11, then
54 % 11 = 10 26 % 11 = 4 93 % 11 = 5
17 % 11 = 6 77 % 11 = 0 31 % 11 = 9
Item Hash
Value
54 10
26 4
93 5
17 6
77 0
31 9
Now when we need to search any element, we just need to divide it by the
table size, and we get the hash value. So we get the O (1) search time.
Now taking one more element 44 when we apply the hash function on 44, we
get (44 % 11 = 0), But 0 hash value already has an element 77. This Problem
is called as Collision.
Collision: According to the Hash Function, two or more item would need in
the same slot. This is said to be called as Collision.
Using a hash function h to map keys to hash-table slots. Because keys K2 and
k5 map to the same slot, they collide.
Why use HashTable?
So Hash Table requires less storage. Indirect addressing element with key k is
stored in slot k with hashing it is stored in h (k) where h is a hash fn and hash
(k) is the value of key k. Hash fn required array range.
Application of Hash Tables
Some application of Hash Tables are:
Methods of Hashing
There are two main methods used to implement hashing:
Insert 5:
h (5) = 5 mod 9 =5
Create a linked list for T [5] and store value 5 in it.
Similarly, insert 28. h (28) = 28 mod 9 =1. Create a Linked List for T [1] and
store value 28 in it. Now insert 19 h (19) = 19 mod 9 = 1. Insert value 19 in
the slot T [1] at the beginning of the linked-list.
Now insert h 15, h (15) = 15 mod 9 = 6. Create a link list for T [6] and store
value 15 in it.
Similarly, insert 20, h (20) = 20 mod 9 = 2 in T [2].
Insert 33, h (33) = 33 mod 9 = 6
In the beginning of the linked list T [6]. Then,
Insert 12, h (12) = 12 mod 9 = 3 in T [3].
Insert 17, h (17) = 17 mod 9 = 8 in T [8].
Insert 10, h (10) = 10 mod 9 = 1 in T [1].
Thus the chained- hash- table after inserting key 10 is
Hash-Insert (T, k)
1. i ← 0
2. repeat j ← h (k, i)
3. if T [j] = NIL
4. then T [j] ← k
5. return j
6. else ← i= i +1
7. until i=m
8. error "hash table overflow"
The procedure HASH-SEARCH takes as input a hash table T and a key k,
returning j if it finds that slot j contains key k or NIL if key k is not present in
table T.
Hash-Search.T (k)
1. i ← 0
2. repeat j ← h (k, i)
3. if T [j] =j
4. then return j
5. i ← i+1
6. until T [j] = NIL or i=m
7. return NIL
1. Linear Probing
2. Quadratic Probing
3. Double Hashing
1. Linear Probing
It is a Scheme in Computer Programming for resolving collision in hash
tables.
Suppose a new record R with key k is to be added to the memory table T but
that the memory locations with the hash address H (k). H is already filled.
Our natural key to resolve the collision is to crossing R to the first available
location following T (h). We assume that the table T with m location is
circular, so that T [i] comes after T [m].
The above collision resolution is called "Linear Probing".
Linear probing is simple to implement, but it suffers from an issue known as
primary clustering. Long runs of occupied slots build up, increasing the
average search time. Clusters arise because an empty slot proceeded by i full
slots gets filled next with probability (i + 1)/m. Long runs of occupied slots
tend to get longer, and the average search time increases.
Given an ordinary hash function h': U {0, 1...m-1}, the method of linear
probing uses the hash function.
h (k, i) = (h' (k) + i) mod m
Where 'm' is the size of hash table and h' (k) = k mod m. for i=0, 1....m-1.
Given key k, the first slot is T [h' (k)]. We next slot T [h' (k) +1] and so on up
to the slot T [m-1]. Then we wrap around to slots T [0], T [1]....until finally
slot T [h' (k)-1]. Since the initial probe position dispose of the entire probe
sequence, only m distinct probe sequences are used with linear probing.
Example
Consider inserting the keys 24, 36, 58,65,62,86 into a hash table of size m=11
using linear probing, consider the primary hash function is h' (k) = k mod m.
Solution
Initial state of hash table
2. Quadratic Probin
Suppose a record R with key k has the hash address H (k) = h then instead of
searching the location with addresses h, h+1, and h+ 2...We linearly search
the locations with addresses
h, h+1, h+4, h+9...h+i2
Quadratic Probing uses a hash function of the form
h (k,i) = (h' (k) + c1i + c2i2) mod m
Where (as in linear probing) h' is an auxiliary hash function c1 and c2 ≠0 are
auxiliary constants and i=0, 1...m-1. The initial position is T [h' (k)]; later
position probed is offset by the amount that depend in a quadratic manner on
the probe number i.
Example
Consider inserting the keys 74, 28, 36,58,21,64 into a hash table of size m
=11 using quadratic probing with c1=1 and c2=3. Further consider that the
primary hash function is h' (k) = k mod m.
Solution
For Quadratic Probing, we have
h (k, i) = [k mod m +c1i +c2 i2) mod m
3. Double Hashing
Double Hashing is one of the best techniques available for open addressing
because the permutations produced have many of the characteristics of
randomly chosen permutations.
Double hashing uses a hash function of the form
h (k, i) = (h1(k) + i h2 (k)) mod m
Where h1 and h2 are auxiliary hash functions and m is the size of the hash
table.
h1 (k) = k mod m or h2 (k) = k mod m'. Here m' is slightly less than m (say m-
1 or m-2).
Example
Consider inserting the keys 76, 26, 37,59,21,65 into a hash table of size m =
11 using double hashing. Consider that the auxiliary hash functions are
h1 (k)=k mod 11 and h2(k) = k mod 9.
Solution
Initial state of Hash table is
1. Insert 76.
h1(76) = 76 mod 11 = 10
h2(76) = 76 mod 9 = 4
h (76, 0) = (10 + 0 x 4) mod 11
= 10 mod 11 = 10
T [10] is free, so insert key 76 at this place.
2. Insert 26.
h1(26) = 26 mod 11 = 4
h2(26) = 26 mod 9 = 8
h (26, 0) = (4 + 0 x 8) mod 11
= 4 mod 11 = 4
T [4] is free, so insert key 26 at this place.
3. Insert 37.
h1(37) = 37 mod 11 = 4
h2(37) = 37 mod 9 = 1
h (37, 0) = (4 + 0 x 1) mod 11 = 4 mod 11 = 4
T [4] is not free, the next probe sequence is
h (37, 1) = (4 + 1 x 1) mod 11 = 5 mod 11 = 5
T [5] is free, so insert key 37 at this place.
4. Insert 59.
h1(59) = 59 mod 11 = 4
h2(59) = 59 mod 9 = 5
h (59, 0) = (4 + 0 x 5) mod 11 = 4 mod 11 = 4
Since, T [4] is not free, the next probe sequence is
h (59, 1) = (4 + 1 x 5) mod 11 = 9 mod 11 = 9
T [9] is free, so insert key 59 at this place.
5. Insert 21.
h1(21) = 21 mod 11 = 10
h2(21) = 21 mod 9 = 3
h (21, 0) = (10 + 0 x 3) mod 11 = 10 mod 11 = 10
T [10] is not free, the next probe sequence is
h (21, 1) = (10 + 1 x 3) mod 11 = 13 mod 11 = 2
T [2] is free, so insert key 21 at this place.
6. Insert 65.
h1(65) = 65 mod 11 = 10
h2(65) = 65 mod 9 = 2
h (65, 0) = (10 + 0 x 2) mod 11 = 10 mod 11 = 10
T [10] is not free, the next probe sequence is
h (65, 1) = (10 + 1 x 2) mod 11 = 12 mod 11 = 1
T [1] is free, so insert key 65 at this place.
Thus, after insertion of all keys the final hash table is
Hash Function
Hash Function is used to index the original value or key and then used later
each time the data associated with the value or key is to be retrieved. Thus,
hashing is always a one-way operation. There is no need to "reverse
engineer" the hash function by analyzing the hashed values.
Characteristics of Good Hash Function
Example
If the hash table has size m = 12 and the key is k = 100, then h (k) = 4. Since
it requires only a single division operation, hashing by division is quite fast.
2. Multiplication Method
The multiplication method for creating hash functions operates in two steps.
First, we multiply the key k by a constant A in the range 0 < A < 1 and
extract the fractional part of kA. Then, we increase this value by m and take
the floor of the result.
The hash function is:
Where "k A mod 1" means the fractional part of k A, that is, k A - ⌊ k A ⌋ .
3. Mid Square Method
The key k is squared. Then function H is defined by
H (k) = L
Where L is obtained by deleting digits from both ends of k2. We emphasize
that the same position of k2 must be used for all of the keys.
4. Folding Method
The key k is partitioned into a number of parts k1, k2.... kn where each part
except possibly the last, has the same number of digits as the required
address.
Then the parts are added together, ignoring the last carry.
H (k) = k1+ k2+.....+kn
Example
Company has 68 employees, and each is assigned a unique four- digit
employee number. Suppose L consist of 2- digit addresses: 00, 01, and
02....99. We apply the above hash functions to each of the following
employee numbers:
3205, 7148, 2345
(a) Division Method: Choose a Prime number m close to 99, such as m =97,
Then
H (3205) = 4, H (7148) = 67, H (2345) = 17.
That is dividing 3205 by 17 gives a remainder of 4, dividing 7148 by 97
gives a remainder of 67, dividing 2345 by 97 gives a remainder of 17.
(b) Mid-Square Method:
k = 3205 7148 2345
2
k = 10272025 51093904 5499025
h (k) = 72 93 99
Observe that fourth & fifth digits, counting from right are chosen for hash
address.
(c) Folding Method: Divide the key k into 2 parts and adding yields the
following hash address:
H (3205) = 32 + 50 = 82 H (7148) = 71 + 84 = 55
H (2345) = 23 + 45 = 68
Example
Working of TREE-INSERT
Suppose we want to insert an item with key 13 into a Binary Search Tree.
x=1
y = 1 as x ≠ NIL.
Key [z] < key [x]
13 < not equal to 12.
x ←right [x].
x ←3
Again x ≠ NIL
y ←3
key [z] < key [x]
13 < 18
x←left [x]
x←6
Again x ≠ NIL, y←6
13 < 15
x←left [x]
x←NIL
p [z]←6
Now our node z will be either left or right child of its parent (y).
key [z] < key [y]
13 < 15
Left [y] ← z
Left [6] ← z
So, insert a node in the left of node index at 6.
5. Deletion in Binary Search Tree: When Deleting a node from a tree it is
essential that any relationships, implicit in the tree can be maintained. The
deletion of nodes from a binary search tree will be considered:
There are three distinct cases:
TREE-DELETE (T, z)
1. If left [z] = NIL or right [z] = NIL.
2. Then y ← z
3. Else y ← TREE- SUCCESSOR (z)
4. If left [y] ≠ NIL.
5. Then x ← left [y]
6. Else x ← right [y]
7. If x ≠NIL
8. Then p[x] ← p [y]
9. If p[y] = NIL.
10. Then root [T] ← x
11. Else if y = left [p[y]]
12. Then left [p[y]] ← x
13. Else right [p[y]] ← y
14. If y ≠ z.
15. Then key [z] ← key [y]
16. If y has other fields, copy them, too.
17. Return y
The Procedure runs in O (h) time on a tree of height h.
Example
Deleting a node z from a binary search tree. Node z may be the root, a left
child of node q, or a right child of q.
Node z has two children (left child l and right child r), and its successor y ≠ r
lies within the subtree rooted at r. We replace y with its own right child x,
and we set y to be r's parent. Then, we set y to be q's child and the parent of l.
A tree T is an almost red-black tree (ARB tree) if the root is red, but other
conditions above hold.
Operations on RB Trees
The search-tree operations TREE-INSERT and TREE-DELETE, when runs
on a red-black tree with n keys, take O (log n) time. Because they customize
the tree, the conclusion may violate the red-black properties. To restore these
properties, we must change the color of some of the nodes in the tree and also
change the pointer structure.
1. Rotation
Restructuring operations on red-black trees can generally be expressed more
clearly in details of the rotation operation.
Insert the new node the way it is done in Binary Search Trees.
Color the node red
If an inconsistency arises for the red-black tree, fix the tree
according to the type of discrepancy.
A discrepancy can decision from a parent and a child both having a red color.
This type of discrepancy is determined by the location of the node concerning
grandparent, and the color of the sibling of the parent.
RB-INSERT (T, z)
1. y ← nil [T]
2. x ← root [T]
3. while x ≠ NIL [T]
4. do y ← x
5. if key [z] < key [x]
6. then x ← left [x]
7. else x ← right [x]
8. p [z] ← y
9. if y = nil [T]
10. then root [T] ← z
11. else if key [z] < key [y]
12. then left [y] ← z
13. else right [y] ← z
14. left [z] ← nil [T]
15. right [z] ← nil [T]
16. color [z] ← RED
17. RB-INSERT-FIXUP (T, z)
After the insert new node, Coloring this new node into black may violate the
black-height conditions and coloring this new node into red may violate
coloring conditions i.e. root is black and red node has no red children. We
know the black-height violations are hard. So we color the node red. After
this, if there is any color violation, then we have to correct them by an RB-
INSERT-FIXUP procedure.
RB-INSERT-FIXUP (T, z)
1. while color [p[z]] = RED
2. do if p [z] = left [p[p[z]]]
3. then y ← right [p[p[z]]]
4. If color [y] = RED
5. then color [p[z]] ← BLACK //Case 1
6. color [y] ← BLACK //Case 1
7. color [p[z]] ← RED //Case 1
8. z ← p[p[z]] //Case 1
9. else if z= right [p[z]]
10. then z ← p [z] //Case 2
11. LEFT-ROTATE (T, z) //Case 2
12. color [p[z]] ← BLACK //Case 3
13. color [p [p[z]]] ← RED //Case 3
14. RIGHT-ROTATE (T,p [p[z]]) //Case 3
15. else (same as then clause)
With "right" and "left" exchanged
16. color [root[T]] ← BLACK
Example
Show the red-black trees that result after successively inserting the keys
41,38,31,12,19,8 into an initially empty red-black tree.
Solution
Insert 41
Insert 19
Delete 41
No Tree.
Dynamic Programming
Dynamic Programming is the most powerful design technique for solving
optimization problems.
Divide & Conquer algorithm partition the problem into disjoint subproblems
solve the subproblems recursively and then combine their solution to solve
the original problems.
Dynamic Programming is used when the subproblems are not independent,
e.g. when they share the same subproblems. In this case, divide and conquer
may do more work than necessary, because it solves the same sub problem
multiple times.
Dynamic Programming solves each subproblems just once and stores the
result in a table so that it can be repeatedly retrieved if needed again.
Dynamic Programming is a Bottom-up approach- we solve all possible small
problems and then combine to obtain solutions for bigger problems.
Dynamic Programming is a paradigm of algorithm design in which an
optimization problem is solved by a combination of achieving sub-problem
solutions and appearing to the "principle of optimality".
Characteristics of Dynamic Programming
Dynamic Programming works when a problem has the following features:-
Fibonacci Sequence
Fibonacci sequence is the sequence of numbers in which every next item is
the total of the previous two items. And each number of the Fibonacci
sequence is called Fibonacci number.
Example
0 ,1,1,2,3,5,8,13,21,....................... is a Fibonacci sequence.
The Fibonacci numbers F_nare defined as follows:
F0 = 0
Fn=1
Fn=F(n-1)+ F(n-2)
FIB (n)
1. If (n < 2)
2. then return n
3. else return FIB (n - 1) + FIB (n - 2)
The Figure shows four levels of recursion for the call fib (8):
It can be observed that the total entries in matrix 'C' is 'pr' as the matrix is of
dimension p x r Also each entry takes O (q) times to compute, thus the total
time to compute all possible entries for the matrix 'C' which is a
multiplication of 'A' and 'B' is proportional to the product of the dimension p
q r.
It is also noticed that we can save the number of operations by reordering the
parenthesis.
Example
Let us have 3 matrices, A1,A2,A3 of order (10 x 100), (100 x 5) and (5 x 50)
respectively.
Three Matrices can be multiplied in two ways:
Or(A1,A2,A3.............An-1) (An)
It can be observed that after splitting the kth matrices, we are left with two
parenthesized sequence of matrices: one consist 'k' matrices and another
consist 'n-k' matrices.
Now there are 'L' ways of parenthesizing the left sublist and 'R' ways of
parenthesizing the right sublist then the Total will be L.R:
c (n) =
On applying Stirling's formula we have
c (n) = Ω
Which shows that 4n grows faster, as it is an exponential function, then n1.5.
Development of Dynamic Programming Algorithm
One possible answer to the first question for finding the best value of 'k' is to
check all possible choices of 'k' and consider the best among them. But that it
can be observed that checking all possibilities will lead to an exponential
number of total possibilities. It can also be noticed that there exists only O
(n2 ) different sequence of matrices, in this way do not reach the exponential
growth.
Step 1: Structure of an optimal parenthesization
Our first step in the dynamic paradigm is to find the optimal substructure and
then use it to construct an optimal solution to the problem from an optimal
solution to subproblems.
Let Ai....j where i≤ j denotes the matrix that results from evaluating the product
Ai Ai+1....Aj.
If i < j then any parenthesization of the product Ai Ai+1 ......Aj must split that
the product between Ak and Ak+1 for some integer k in the range i ≤ k ≤ j.
That is for some value of k, we first compute the matrices Ai.....k & Ak+1....j and
then multiply them together to produce the final product Ai....j. The cost of
computing Ai....k plus the cost of computing Ak+1....j plus the cost of multiplying
them together is the cost of parenthesization.
Step 2: A Recursive Solution
Let m [i, j] be the minimum number of scalar multiplication needed to
compute the matrixAi....j.
If i=j the chain consist of just one matrix Ai....i=Ai so no scalar multiplication
are necessary to compute the product. Thus m [i, j] = 0 for i= 1, 2, 3....n.
If i<j we assume that to optimally parenthesize the product we split it
between Ak and Ak+1 where i≤ k ≤j. Then m [i,j] equals the minimum cost for
computing the subproducts Ai....k and Ak+1....j+ cost of multiplying them
together. We know Ai has dimension pi-1 x pi, so computing the product
Ai....k and Ak+1....jtakes pi-1 pk pj scalar multiplication, we obtain
m [i,j] = m [i, k] + m [k + 1, j] + pi-1 pk pj
There are only (j-1) possible values for 'k' namely k = i, i+1.....j-1. Since the
optimal parenthesization must use one of these values for 'k' we need only
check them all to find the best.
So the minimum cost of parenthesizing the product Ai Ai+1......Aj becomes
Let us proceed with working away from the diagonal. We compute the
optimal solution for the product of 2 matrices.
We initialize the diagonal element with equal i,j value with '0'.
After that second diagonal is sorted out and we get all the
values corresponded to it
Now the third diagonal will be solved out in the same way.
Now product of 3 matrices:
M [1, 3] = M1 M2 M3
1. There are two cases by which we can solve this multiplication:
( M1 x M2) + M3, M1+ (M2x M3)
2. After solving both cases we choose the case in which minimum
output is there.
M [1, 3] =264
As Comparing both output 264 is minimum in both cases so we insert 264 in
table and ( M1 x M2) + M3 this combination is chosen for the output making.
M [2, 4] = M2 M3 M4
There are two cases by which we can solve this multiplication: (M2x M3)+M4,
M2+(M3 x M4)
After solving both cases we choose the case in which minimum output is
there.
M [2, 4] = 1320
As Comparing both output 1320 is minimum in both cases so we
insert 1320 in table and M2+(M3 x M4) this combination is chosen for the
output making.
M [3, 5] = M3 M4 M5
There are two cases by which we can solve this multiplication:
( M3 x M4) + M5, M3+ ( M4xM5)
After solving both cases we choose the case in which minimum output is
there.
M [3, 5] = 1140
As Comparing both output 1140 is minimum in both cases so we
insert 1140 in table and ( M3 x M4) + M5this combination is chosen for the
output making.
After solving these cases we choose the case in which minimum output is
there
M [1, 4] =1080
As comparing the output of different cases then '1080' is minimum output, so
we insert 1080 in the table and (M1 xM2) x (M3 x M4) combination is taken
out in output making,
M [2, 5] = M2 M3 M4 M5
There are three cases by which we can solve this multiplication:
(M2 x M3 x M4)x M5
M2 x( M3 x M4 x M5)
(M2 x M3)x ( M4 x M5)
After solving these cases we choose the case in which minimum output is
there
M [2, 5] = 1350
As comparing the output of different cases then '1350' is minimum output, so
we insert 1350 in the table and M2 x( M3 x M4 xM5)combination is taken out
in output making.
Now Product of 5 matrices:
M [1, 5] = M1 M2 M3 M4 M5
M [1, 5] = 1344
As comparing the output of differrent cases then '1344' is minimum output,
so we insert 1344 in the table and M1 x M2 x(M3 x M4 x M5)combination is
taken out in output making.
Final Output is:
Computing Optimal Costs: let us assume that matrix Ai has dimension pi-1x
pi for i=1, 2, 3....n. The input is a sequence (p0,p1,......pn) where length [p] =
n+1. The procedure uses an auxiliary table m [1....n, 1.....n] for storing m [i, j]
costs an auxiliary table s [1.....n, 1.....n] that record which index of k achieved
the optimal costs in computing m [i, j].
The algorithm first computes m [i, j] ← 0 for i=1, 2, 3.....n, the minimum
costs for the chain of length 1.
Algorithm of Matrix Chain Multiplication
MATRIX-CHAIN-ORDER (p)
1. n length[p]-1
2. for i ← 1 to n
3. do m [i, i] ← 0
4. for l ← 2 to n // l is the chain length
5. do for i ← 1 to n-l + 1
6. do j ← i+ l -1
7. m[i,j] ← ∞
8. for k ← i to j-1
9. do q ← m [i, k] + m [k + 1, j] + pi-1 pk pj
10. If q < m [i,j]
11. then m [i,j] ← q
12. s [i,j] ← k
13. return m and s.
We will use table s to construct an optimal solution.
Step 1: Constructing an Optimal Solution
PRINT-OPTIMAL-PARENS (s, i, j)
1. if i=j
2. then print "A"
3. else print "("
4. PRINT-OPTIMAL-PARENS (s, i, s [i, j])
5. PRINT-OPTIMAL-PARENS (s, s [i, j] + 1, j)
6. print ")"
Analysis: There are three nested loops. Each loop executes a maximum n
times.
1. l, length, O (n) iterations.
2. i, start, O (n) iterations.
3. k, split point, O (n) iterations
But 28 < ∞
So m [i,j] ← q
And q ← 28
m [2, 4] ← 28
and s [2, 4] ← 3
e. It means in s table at s [2,4] insert 3 and at m [2,4] insert 28.
Case 3: l becomes 4
L=4
For i ← 1 to n-l + 1
i ← 1 to 4 - 4 + 1
i←1
i=1
do j ← i + l - 1
j←1+4-1
j←4
j=4
Now m [i,j] ← ∞
m [1,4] ← ∞
for k ← i to j -1
k ← 1 to 4 - 1
k ← 1 to 3
When k = 1
q ← m [i, k] + m [k + 1, j] + pi-1 pk pj
q ← m [1,1] + m [2,4] + p0xp4x p1
q ← 0 + 28 + 7 x 2 x 1
q ← 42
Compare q and m [i, j]
m [i,j] was ∞
i.e. m [1,4]
if q < m [1,4]
42< ∞
True
Then m [i,j] ← q
m [1,4] ← 42
and s [1,4] 1 ? k =1
When k = 2
L = 4, i=1, j = 4
q ← m [i, k] + m [k + 1, j] + pi-1 pk pj
q ← m [1, 2] + m [3,4] + p0 xp2 xp4
q ← 35 + 40 + 7 x 5 x 2
q ← 145
Compare q and m [i,j]
Now m [i, j]
i.e. m [1,4] contains 42.
So if q < m [1, 4]
But 145 less than or not equal to m [1, 4]
So 145 less than or not equal to 42.
So no change occurs.
When k = 3
l=4
i=1
j=4
q ← m [i, k] + m [k + 1, j] + pi-1 pk pj
q ← m [1, 3] + m [4,4] + p0 xp3 x p4
q ← 48 + 0 + 7 x 4 x 2
q ← 114
Again q less than or not equal to m [i, j]
i.e. 114 less than or not equal to m [1, 4]
114 less than or not equal to 42
So no change occurs. So the value of m [1, 4] remains 42. And value of s [1,
4] = 1
Now we will make use of only s table to get an optimal solution.
Longest Common Sequence (LCS)
A subsequence of a given sequence is just the given sequence with some
elements left out.
Given two sequences X and Y, we say that the sequence Z is a common
sequence of X and Y if Z is a subsequence of both X and Y.
In the longest common subsequence problem, we are given two sequences X
= (x1 x2....xm) and Y = (y1 y2 yn) and wish to find a maximum length common
subsequence of X and Y. LCS Problem can be solved using dynamic
programming.
Characteristics of Longest Common Sequence
A brute-force approach we find all the subsequences of X and check each
subsequence to see if it is also a subsequence of Y, this approach requires
exponential time making it impractical for the long sequence.
Given a sequence X = (x1 x2.....xm) we define the ith prefix of X for i=0, 1,
and 2...m as Xi= (x1 x2.....xi). For example: if X = (A, B, C, B, C, A, B, C)
then X4= (A, B, C, B)
Optimal Substructure of an LCS: Let X = (x1 x2....xm) and Y = (y1 y2.....) yn)
be the sequences and let Z = (z1 z2......zk) be any LCS of X and Y.
If xm = yn, then zk=x_m=yn and Zk-1 is an LCS of Xm-1and Yn-1
If xm ≠ yn, then zk≠ xm implies that Z is an LCS of Xm-1and Y.
If xm ≠ yn, then zk≠yn implies that Z is an LCS of X and Yn-1
Step 2: Recursive Solution: LCS has overlapping subproblems property
because to find LCS of X and Y, we may need to find the LCS of Xm-1 and
Yn-1. If xm ≠ yn, then we must solve two subproblems finding an LCS of X
and Yn-1.Whenever of these LCS's longer is an LCS of x and y. But each of
these subproblems has the subproblems of finding the LCS of Xm-1 and Yn-1.
Let c [i,j] be the length of LCS of the sequence Xiand Yj.If either i=0 and j
=0, one of the sequences has length 0, so the LCS has length 0. The optimal
substructure of the LCS problem given the recurrence formula
Step 3: Computing the length of an LCS: let two sequences X = (x1 x2.....xm)
and Y = (y1 y2..... yn) as inputs. It stores the c [i,j] values in the table c
[0......m,0..........n].Table b [1..........m, 1..........n] is maintained which help us
to construct an optimal solution. c [m, n] contains the length of an LCS of
X,Y.
Algorithm of Longest Common Sequence
LCS-LENGTH (X, Y)
1. m ← length [X]
2. n ← length [Y]
3. for i ← 1 to m
4. do c [i,0] ← 0
5. for j ← 0 to m
6. do c [0,j] ← 0
7. for i ← 1 to m
8. do for j ← 1 to n
9. do if xi= yj
10. then c [i,j] ← c [i-1,j-1] + 1
11. b [i,j] ← "↖"
12. else if c[i-1,j] ≥ c[i,j-1]
13. then c [i,j] ← c [i-1,j]
14. b [i,j] ← "↑"
15. else c [i,j] ← c [i,j-1]
16. b [i,j] ← "← "
17. return c and b.
Example of Longest Common Sequence
Given two sequences X [1...m] and Y [1.....n]. Find the longest common
subsequences to both.
Example
Determine the LCS of (1,0,0,1,0,1,0,1) and (0,1,0,1,1,0,1,1,0).
Solution
let X = (1,0,0,1,0,1,0,1) and Y = (0,1,0,1,1,0,1,1,0).
From the table we can deduct that LCS = 6. There are several such
sequences, for instance (1,0,0,1,1,0) (0,1,0,1,0,1) and (0,0,1,1,0,1)
W ≤ capacity
Value ← Max
Input
Knapsack of capacity
List (Array) of weight and their corresponding value.
The maximum value of items in the knapsack is 40, the bottom-right entry).
The dynamic programming approach can now be coded as the following
algorithm:
Algorithm of Knapsack Problem
KNAPSACK (n, W)
1. for w = 0, W
2. do V [0,w] ← 0
3. for i=0, n
4. do V [i, 0] ← 0
5. for w = 0, W
6. do if (wi≤ w & vi + V [i-1, w - wi]> V [i -1,W])
7. then V [i, W] ← vi + V [i - 1, w - wi]
8. else V [i, W] ← V [i - 1, w]
Cases
If array [mid] =0, then swap array [mid] with array [low] and
increment both pointers once.
If array [mid] = 1, then no swapping is required. Increment mid
pointer once.
If array [mid] = 2, then we swap array [mid] with array [high]
and decrement the high pointer once.
Code
C
#include<bits/stdc++.h>
using namespace std;
// Function to sort the input array where the array is assumed to have values
in {0, 1, 2}
// We have to take 3 distint or unique elements
void JTP(int arr[], int arr_size)
{
int low = 0;
int high = arr_size - 1;
int mid = 0;
// We have keep iterating till all the elements are sorted
while (mid <= high)
{
switch (arr[mid])
{
// Here mid is 0.
case 0:
swap(arr[low++], arr[mid++]);
break;
// Here mid is 1.
case 1:
mid++;
break;
// Here mid is 2.
case 2:
swap(arr[mid], arr[high--]);
break;
}
}
}
// Now, we write the function to print array arr[]
void printArray(int arr[], int arr_size)
{
// To iterate and print every element, we follow these steps
for (int i = 0; i < arr_size; i++)
cout << arr[i] << " ";
}
//Main Code
int main()
{
int arr[] = {0,1,0,1,2,0,1,2};
int n = sizeof(arr)/sizeof(arr[0]);
cout << "Array before executing the algorithm: ";
printArray(arr, n);
JTP(arr, n);
cout << "\nArray after executing the DNFS algorithm: ";
printArray(arr, n);
return 0;
}
Output
Array before executing the algorithm: 0 1 0 1 2 0 1 2
Array after executing the DNFS algorithm: 0 0 0 1 1 1 2 2
Java
import java.io.*;
class DNF {
static void JTP(int arr[], int arr_size)
{
int low = 0;
int high = arr_size - 1;
int mid = 0, temp=0; // We use temporary variable for swapping
while (mid <= high)
{
switch (arr[mid])
{
case 0: // Here mid pointer points is at 0.
{
temp = arr[low];
arr[low] = arr[mid];
arr[mid] = temp;
low++;
mid++;
break;
}
case 1: // Here mid pointer points is at 1.
mid++;
break;
case 2: // Here mid pointer points is at 2.
{
temp = arr[mid];
arr[mid] = arr[high];
arr[high] = temp;
high--;
break;
}
}
}
}
// Now we have to call function to print array arr[]
static void printArray(int arr[], int arr_size)
{
int i;
for (i = 0; i < arr_size; i++)
System.out.print(arr[i]+" ");
System.out.println("");
}
//Now we use driver function to check for above functions
public static void main (String[] arguments)
{
int arr[] = {0, 1, 0, 1, 2, 0, 1, 2};
int arr_size = arr.length;
System.out.println("Array before executing the DNFS algorithm : ");
printArray(arr, arr_size);
JTP(arr, arr_size);
System.out.println("\nArray after executing the DNFS algorithm : ");
printArray(arr, arr_size);
}
}
Output
Array before executing the DNFS algorithm : 0 1 0 1 2 0 1 2
Array after executing the DNFS algorithm : 0 0 0 1 1 1 2 2
Python
def JTP( arr, arr_size):
low = 0
high = arr_size - 1
mid = 0
while mid <= high:
if arr[mid] == 0:
arr[low],arr[mid] = arr[mid],arr[low]
low = low + 1
mid = mid + 1
elif arr[mid] == 1:
mid = mid + 1
else:
arr[mid],arr[high] = arr[high],arr[mid]
high = high - 1
return arr
# Function to print array
def printArray(arr):
for k in arr:
print k,
print
# Driver Program
arr = [0, 1, 0, 1, 1, 2, 0, 1, 2]
arr_size = len(arr)
print " Array before executing the algorithm: ",
printArray(arr)
arr = JTP(arr, arr_size)
print "Array after executing the DNFS algorithm: ",
printArray(arr)
Output
Array before executing the algorithm: 0 1 0 1 1 2 0 1 2
Array after executing the DNFS algorithm: 0 0 0 1 1 1 1 2 2
Optimal Substructure
It satisfies overlapping subproblem properties. In a two dimensional array,
Longest Common Subsequence can be made as a memo, where LCS[X][Y]
represents the length between original with length X and reverse, with length.
The longest palindromic subsequence can be generated by backtracking
technique, after filling and using the above algorithm.
Greedy Algorithm
"Greedy Method finds out of many options, but you have to choose the best
option."
In this method, we have to find out the best method/option out of many
present ways.
In this approach/method we focus on the first stage and decide the output,
don't think about the future. This method may or may not give the best
output.
Greedy Algorithm solves problems by making the best choice that seems best
at the particular moment. Many optimization problems can be determined
using a greedy algorithm. Some issues have no efficient solution, but a
greedy algorithm may provide a solution that is close to optimal. A greedy
algorithm works if a problem exhibits the following two properties:
Example
1. Machine Scheduling
2. Fractional Knapsack Problem
3. Minimum Spanning Tree
4. Huffman Code
5. Job Sequencing
6. Activity Selection Problem
Example
Given 10 activities along with their start and end time as
S = (A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)
Si = (1,2,3,4,7,8,9,9,11,12)
fi = (3,5,4,7,10,9,11,13,12,14)
Compute a schedule where the greatest number of activities takes place.
Solution
The solution to the above Activity scheduling problem using a greedy
strategy is illustrated below:
Arranging the activities in increasing order of end time
Now, schedule A1
Next schedule A3 as A1 and A3 are non-interfering.
Next skip A2 as it is interfering.
Next, schedule A4 as A1 A3 and A4 are non-interfering, then next, schedule A6
as A1 A3 A4 and A6 are non-interfering.
Skip A5 as it is interfering.
Next, schedule A7 as A1 A3 A4 A6 and A7 are non-interfering.
Next, schedule A9 as A1 A3 A4 A6 A7 and A9 are non-interfering.
Skip A8 as it is interfering.
Next, schedule A10 as A1 A3 A4 A6 A7 A9 and A10 are non-interfering.
Thus the final Activity schedule is:
Fractional Knapsack
Fractions of items can be taken rather than having to make binary (0-1)
choices for each item.
Fractional Knapsack Problem can be solvable by greedy strategy whereas 0 -
1 problem is not.
Steps to Solve the Fractional Problem
1. Compute the value per pound for each item.
2. Obeying a Greedy Strategy, we take as possible of the item
with the highest value per pound.
3. If the supply of that element is exhausted and we can still carry
more, we take as much as possible of the element with the next
value per pound.
4. Sorting, the items by value per pound, the greedy algorithm run
in O (n log n) time.
Huffman Codes
(i) Data can be encoded efficiently using Huffman Codes.
(ii) It is a widely used and beneficial technique for compressing data.
(iii) Huffman's greedy algorithm uses a table of the frequencies of
occurrences of each character to build up an optimal way of representing each
character as a binary string.
Suppose we have 105 characters in a data file. Normal Storage: 8 bits per
character (ASCII) - 8 x 105 bits in a file. But we want to compress the file and
save it compactly. Suppose only six characters appear in the file:
The algorithm builds the tree T analogous to the optimal code in a bottom-up
manner. It starts with a set of |C| leaves (C is the number of characters) and
performs |C| - 1 'merging' operations to create the final tree. In the Huffman
algorithm 'n' denotes the quantity of a set of characters, z indicates the parent
node, and x & y are the left & right child of z respectively.
Algorithm of Huffman Code
Huffman (C)
1. n=|C|
2. Q ← C
3. for i=1 to n-1
4. do
5. z= allocate-Node ()
6. x= left[z]=Extract-Min(Q)
7. y= right[z] =Extract-Min(Q)
8. f [z]=f[x]+f[y]
9. Insert (Q, z)
10. return Extract-Min (Q)
Example
Find an optimal Huffman Code for the following set of frequencies:
a: 50 b: 25 c: 15 d: 40 e: 75
Solution
i.e.
Again for i=2
Similarly, we apply the same process we get
Thus, the final output is:
Here we find a schedule for S that minimizes the total penalty incurred for
missed deadlines.
A task is late in this schedule if it finished after its deadline. Otherwise, the
task is early in the schedule. An arbitrary schedule can consistently be put
into early-first form, in which the first tasks precede the late tasks, i.e., if
some new task x follows some late task y, then we can switch the position of
x and y without affecting x being early or y being late.
An arbitrary schedule can always be put into a canonical form in which first
tasks precede the late tasks, and first tasks are scheduled in order of
nondecreasing deadlines.
A set A of tasks is independent if there exists a schedule for the particular
tasks such that no tasks are late. So the set of first tasks for a schedule forms
an independent set of tasks 'l' denote the set of all independent set of tasks.
For any set of tasks A, A is independent if for t = 0, 1, 2.....n we have Nt(A) ≤
t where Nt(A) denotes the number of tasks in A whose deadline is t or prior,
i.e. if the tasks in A are expected in order of monotonically growing
deadlines, then no task is late.
Example
Find the optimal schedule for the following task with given weight (penalties)
and deadlines.
1 2 3 4 5 6 7
di 4 2 4 3 1 4 6
wi 70 60 50 40 30 20 10
Solution
According to the Greedy algorithm we sort the jobs in decreasing order of
their penalties so that minimum of penalties will be charged.
In this problem, we can see that the maximum time for which uniprocessor
machine will run in 6 units because it is the maximum deadline.
Let Ti represents the tasks where i = 1 to 7
Other schedule is
(2 4 1 3 7 5 6)
There can be many other schedules but (2 4 1 3 7 5 6) is optimal.
Solution
The cost- adjacency matrix of graph G is as follows:
costij =
The tour starts from area H1 and then select the minimum cost area reachable
from H1.
Mark area H6 because it is the minimum cost area reachable from H1 and then
select minimum cost area reachable from H6.
Mark area H7 because it is the minimum cost area reachable from H6 and then
select minimum cost area reachable from H7.
Mark area H8 because it is the minimum cost area reachable from H8.
Mark area H5 because it is the minimum cost area reachable from H5.
Mark area H2 because it is the minimum cost area reachable from H2.
Mark area H3 because it is the minimum cost area reachable from H3.
Mark area H4 and then select the minimum cost area reachable from H4 it is
H1.So, using the greedy strategy, we get the following.
4 3 2 4 3 2 1 6
H1 → H6 → H7 → H8 → H5 → H2 → H3 → H4 → H1.
Backtracking
The Backtracking is an algorithmic-method to solve a problem with an
additional way. It uses a recursive approach to explain the problems. We can
say that the backtracking is needed to find all possible combination to solve
an optimization problem.
Backtracking is a systematic way of trying out different sequences of
decisions until we find one that "works."
In the following Figure:
Generally, however, we draw our trees downward, with the root at the top.
Principle of Maze
As explained above, in the maze we have to travel from starting point to
ending point. The problem is to choose the path. If we find any dead-end
before ending point, we have to backtrack and move the direction. The
direction for traversing is North, East, West, and South. We have to continue
"move and backtrack" until we reach the final point.
Consider that we are having a two-dimensional maze cell [WIDTH]
[HEIGHT]. Here cell [x] [y] = 1 denotes wall and cell [x] [y] = 0 denotes free
cell in the particular location x, y in the maze. The directions we can change
in the array are North, East, West, and South. The first step is to make the
boundary of the two - dimensional array as one so that we won't go out of the
maze and usually reside inside the maze at any time.
Example Maze
1 1 1 1 1 1 1
1 0 0 0 1 1 1
1 1 1 0 1 1 1
1 1 1 0 0 0 1
1 1 1 1 1 0 1
1 1 1 1 1 1 1
Now start changing from the starting position (since the boundary is filled by
1) and find the next free cell then turn to the next free cell and so on. If we
grasp a dead-end, we have to backtrack and make the cells in the path as 1
(wall). Continue the same process till the final point is reached.
Subset-Sum Problem
The Subset-Sum Problem is to find a subset's' of the given set S =
(S1 S2 S3...Sn) where the elements of the set S are n positive integers in such a
manner that s' ∈ S and sum of the elements of subset's' is equal to some
positive integer 'X.'
The Subset-Sum Problem can be solved by using the backtracking approach.
In this implicit tree is a binary tree. The root of the tree is selected in such a
way that represents that no decision is yet taken on any input. We assume that
the elements of the given set are arranged in increasing order:
S1 ≤ S2 ≤ S3... ≤ Sn
The left child of the root node indicated that we have to include 'S1' from the
set 'S' and the right child of the root indicates that we have to execute 'S1'.
Each node stores the total of the partial solution elements. If at any stage the
sum equals to 'X' then the search is successful and terminates.
The dead end in the tree appears only when either of the two inequalities
exists:
The sum of s' is too large i.e.
s'+ Si + 1 > X
The sum of s' is too small i.e.
Example
Given a set S = (3, 4, 5, 6) and X =9. Obtain the subset sum using
Backtracking approach.
Solution
Initially S = (3, 4, 5, 6) and X =9.
S'= ( ∅ )
The implicit binary tree for the subset sum problem is shown as fig:
The number inside a node is the sum of the partial solution elements at a
particular level.
Thus, if our partial solution elements sum is equal to the positive integer 'X'
then at that time search will terminate, or it continues if all the possible
solution needs to be obtained.
N-Queens Problem
N - Queens problem is to place n - queens in such a manner on an n x n
chessboard that no queens attack each other by being in the same row,
column or diagonal.
It can be seen that for n =1, the problem has a trivial solution, and no solution
exists for n =2 and n =3. So first we will consider the 4 queens problem and
then generate it to n - queens problem.
Given a 4 x 4 chessboard and number the rows and column of the chessboard
1 through 4.
The implicit tree for 4 - queen problem for a solution (2, 4, 1, 3) is as follows:
Fig shows the complete state space for 4 - queens problem. But we can use
backtracking method to generate the necessary node and stop if the next node
violates the rule, i.e., if two queens are attacking.
Place (k, i) returns a Boolean value that is true if the kth queen can be placed
in column i. It tests both whether i is distinct from all previous costs x1,
x2,....xk-1 and whether there is no other queen on the same diagonal.
Using place, we give a precise solution to then n- queens problem.
Place (k, i)
{
For j ← 1 to k - 1
do if (x [j] = i)
or (Abs x [j]) - i) = (Abs (j - k))
then return false;
return true;
}
Place (k, i) return true if a queen can be placed in the kth row and ith column
otherwise return is false.
x [] is a global array whose final k - 1 values have been set. Abs (r) returns
the absolute value of r.
N - Queens (k, n)
{
For i ← 1 to n
do if Place (k, i) then
{
x [k] ← i;
if (k ==n) then
write (x [1....n));
else
N - Queens (k + 1, n);
}
}
Spanning Tree
Given a connected undirected graph, a spanning tree of that graph is a
subgraph that is a tree and joined all vertices. A single graph can have many
spanning trees.
Example
For the above-connected graph. There can be multiple spanning Trees like
Addition of even one single edge results in the spanning tree losing its
property of Acyclicity and elimination of one single edge results in its losing
the property of connectivity.
Minimum Spanning Tree
Minimum Spanning Tree is a Spanning Tree which has minimum total cost.
If we have a linked undirected graph with a weight (or cost) combine with
each edge. Then the cost of spanning tree would be the sum of the cost of its
edges.
Application of Minimum Spanning Tree
Electric Power
Water
Telephone lines
Sewage lines
To reduce cost, you can connect houses with minimum cost
spanning trees.
1. Kruskal's Algorithm
2. Prim's Algorithm
Kruskal's Algorithm
An algorithm to construct a Minimum Spanning Tree for a connected
weighted graph. It is a Greedy Algorithm. The Greedy Choice is to put the
smallest weight edge that does not because a cycle in the MST constructed so
far.
If the graph is not linked, then it finds a Minimum Spanning Tree.
Steps for finding MST using Kruskal's Algorithm
Solution
First we initialize the set A to the empty set and create |v| trees, one
containing each vertex with MAKE-SET procedure. Then sort the edges in E
into order by non-decreasing weight.
There are 9 vertices and 12 edges. So MST formed (9-1) = 8 edges
Now, check for each edge (u, v) whether the endpoints u and v belong to the
same tree. If they do then the edge (u, v) cannot be supplementary.
Otherwise, the two vertices belong to different trees, and the edge (u, v) is
added to A, and the vertices in two trees are merged in by union procedure.
Step 1: So, first take (h, g) edge
Step 4: Now, edge (h, i). Both h and i vertices are in the same set. Thus it
creates a cycle. So this edge is discarded.
Then edge (c, d), (b, c), (a, h), (d, e), (e, f) are considered, and the forest
becomes.
Step 5: In (e, f) edge both endpoints e and f exist in the same tree so
discarded this edge. Then (b, h) edge, it also creates a cycle.
Step 6: After that edge (d, f) and the final spanning tree is shown as in dark
lines.
Prim's Algorithm
It is a greedy algorithm. It starts with an empty spanning tree. The idea is to
maintain two sets of vertices:
At every step, it considers all the edges and picks the minimum weight edge.
After picking the edge, it moves the other endpoint of edge to set containing
MST.
Robert Clay Prim
Steps for finding MST using Prim's Algorithm
MST-PRIM (G, w, r)
1. for each u ∈ V [G]
2. do key [u] ← ∞
3. π [u] ← NIL
4. key [r] ← 0
5. Q ← V [G]
6. While Q ? ∅
7. do u ← EXTRACT - MIN (Q)
8. for each v ∈ Adj [u]
9. do if v ∈ Q and w (u, v) < key [v]
10. then π [v] ← u
11. key [v] ← w (u, v)
Example
Generate minimum cost spanning tree for the following graph using Prim's
algorithm.
Solution
In Prim's algorithm, first we initialize the priority Queue Q. to contain all the
vertices and the key of each vertex to ∞ except for the root, whose key is set
to 0. Suppose 0 vertex is the root, i.e., r. By EXTRACT - MIN (Q) procure,
now u = r and Adj [u] = {5, 1}.
Removing u from set Q and adds it to set V - Q of vertices in the tree. Now,
update the key and π fields of every vertex v adjacent to u but not in a tree.
Taking 0 as starting vertex
Root = 0
Adj [0] = 5, 1
Parent, π [5] = 0 and π [1] = 0
Key [5] = ∞ and key [1] = ∞
w [0, 5) = 10 and w (0,1) = 28
w (u, v) < key [5] , w (u, v) < key [1]
Key [5] = 10 and key [1] = 28
So update key value of 5 and 1 is:
u = EXTRACT_MIN (2, 6)
u=2 [key [2] < key [6]]
12 < 18
Now the root is 2
Adj [2] = {3, 1}
3 is already in a heap
Taking 1, key [1] = 28
w (2,1) = 16
w (2,1) < key [1]
So update key value of key [1] as 16 and its parent as 2.
π[1]= 2
Total Cost = 10 + 25 + 22 + 12 + 16 + 14 = 99
Shortest Paths
In a shortest- paths problem, we are given a weighted, directed graphs G =
(V, E), with weight function w: E → R mapping edges to real-valued
weights. The weight of path p = (v0,v1,..... vk) is the total of the weights of its
constituent edges:
1. -∞
2. Not less than 0.
Shortest paths are not naturally unique, and neither is shortest - paths trees.
Properties of Shortest Path
1. Optimal substructure property: All subpaths of shortest paths are shortest
paths.
(b) Here, v. d < u. d + w (u, v) before relaxing the edge, and so the relaxation
step leaves v. d unchanged.
The subsequent code performs a relaxation step on edge (u, v)
RELAX (u, v, w)
1. If d [v] > d [u] + w (u, v)
2. then d [v] ← d [u] + w (u, v)
3. π [v] ← u
Dijkstra's Algorithm
It is a greedy algorithm that solves the single-source shortest path problem
for a directed graph G = (V, E) with nonnegative edge weights, i.e., w (u, v)
≥ 0 for each edge (u, v) ∈ E.
Dijkstra's Algorithm maintains a set S of vertices whose final shortest - path
weights from the source s have already been determined. That's for all
vertices v ∈ S; we have d [v] = δ (s, v). The algorithm repeatedly selects
the vertex u ∈ V - S with the minimum shortest - path estimate, insert u
into S and relaxes all edges leaving u.
Because it always chooses the "lightest" or "closest" vertex in V - S to insert
into set S, it is called as the greedy strategy.
Dijkstra's Algorithm (G, w, s)
1. INITIALIZE - SINGLE - SOURCE (G, s)
2. S← ∅
3. Q←V [G]
4. while Q ≠ ∅
5. do u ← EXTRACT - MIN (Q)
6. S ← S ∪ {u}
7. for each vertex v ∈ Adj [u]
8. do RELAX (u, v, w)
Analysis
The running time of Dijkstra's algorithm on a graph with edges E and vertices
V can be expressed as a function of |E| and |V| using the Big - O notation.
The simplest implementation of the Dijkstra's algorithm stores vertices of set
Q in an ordinary linked list or array, and operation Extract - Min (Q) is
simply a linear search through all vertices in Q. In this case, the running time
is O (|V2 |+|E|=O(V2 ).
Example
Solution
Step 1: Q =[s, t, x, y, z]
We scanned vertices one by one and find out its adjacent. Calculate the
distance of each adjacent to the source vertices.
We make a stack, which contains those vertices which are selected after
computation of shortest distance.
Firstly we take's' in stack M (which is a source)
M = [S] Q = [t, x, y, z]
Step 2: Now find the adjacent of s that are t and y.
Adj [s] → t, y [Here s is u and t and y are v]
Case - (i) s → t
d [v] > d [u] + w [u, v]
d [t] > d [s] + w [s, t]
∞ > 0 + 10 [false condition]
Then d [t] ← 10
π [t] ← 5
Adj [s] ← t, y
Case - (ii) s→ y
d [v] > d [u] + w [u, v]
d [y] > d [s] + w [s, y]
∞>0+5 [false condition]
∞>5
Then d [y] ← 5
π [y] ← 5
By comparing case (i) and case (ii)
Adj [s] → t = 10, y = 5
y is shortest
y is assigned in 5 = [s, y]
Step 3: Now find the adjacent of y that is t, x, z.
Adj [y] → t, x, z [Here y is u and t, x, z are v]
Case - (i) y →t
d [v] > d [u] + w [u, v]
d [t] > d [y] + w [y, t]
10 > 5 + 3
10 > 8
Then d [t] ← 8
π [t] ← y
Case - (ii) y → x
d [v] > d [u] + w [u, v]
d [x] > d [y] + w [y, x]
∞>5+9
∞ > 14
Then d [x] ← 14
π [x] ← 14
Case - (iii) y → z
d [v] > d [u] + w [u, v]
d [z] > d [y] + w [y, z]
∞>5+2
∞>7
Then d [z] ← 7
π [z] ← y
By comparing case (i), case (ii) and case (iii)
Adj [y] → x = 14, t = 8, z =7
z is shortest
z is assigned in 7 = [s, z]
Bellman-Ford Algorithm
Solves single shortest path problem in which edge weight may be negative
but no negative cycle exists.
This algorithm works correctly when some of the edges of the directed graph
G may have negative weight. When there are no cycles of negative weight,
then we can find out the shortest path between source and destination.
It is slower than Dijkstra's Algorithm but more versatile, as it capable of
handling some of the negative weight edges.
This algorithm detects the negative cycle in a graph and reports their
existence.
Based on the "Principle of Relaxation" in which more accurate values
gradually recovered an approximation to the proper distance by until
eventually reaching the optimum solution.
Given a weighted directed graph G = (V, E) with source s and weight
function w: E → R, the Bellman-Ford algorithm returns a Boolean value
indicating whether or not there is a negative weight cycle that is attainable
from the source. If there is such a cycle, the algorithm produces the shortest
paths and their weights. The algorithm returns TRUE if and only if a graph
contains no negative - weight cycles that are reachable from the source.
Recurrence Relation
distk [u] = [min[distk-1 [u],min[ distk-1 [i]+cost [i,u]]] as i except u.
k → k is the source vertex
u → u is the destination vertex
i → no of edges to be scanned concerning a vertex.
BELLMAN -FORD (G, w, s)
1. INITIALIZE - SINGLE - SOURCE (G, s)
2. for i ← 1 to |V[G]| - 1
3. do for each edge (u, v) ∈ E [G]
4. do RELAX (u, v, w)
5. for each edge (u, v) ∈ E [G]
6. do if d [v] > d [u] + w (u, v)
7. then return FALSE.
8. return TRUE.
Example
Here first we list all the edges and their weights.
Solution
distk [u] = [min[distk-1 [u],min[distk-1 [i]+cost [i,u]]] as i ≠ u.
Step1: To topologically sort vertices apply DFS (Depth First Search) and then
arrange vertices in linear order by decreasing order of finish time.
Now, take each vertex in topologically sorted order and relax each edge.
adj [t] → r, x
3+1<∞
d [r] ← 4
3+5≤2
adj [x] → y
2-3<∞
d [y] ← -1
adj [y] → r
-1 + 4 < 4
3 <4
d [r] ← 3
Floyd-Warshall Algorithm
Let the vertices of G be V = {1, 2........n} and consider a subset {1, 2........k}
of vertices for some k. For any pair of vertices i, j ∈ V, considered all paths
from i to j whose intermediate vertices are all drawn from {1, 2.......k}, and
let p be a minimum weight path from amongst them. The Floyd-Warshall
algorithm exploits a link between path p and shortest paths from i to j with all
intermediate vertices in the set {1, 2.......k-1}. The link depends on whether or
not k is an intermediate vertex of path p.
If k is not an intermediate vertex of path p, then all intermediate vertices of
path p are in the set {1, 2........k-1}. Thus, the shortest path from vertex i to
vertex j with all intermediate vertices in the set {1, 2.......k-1} is also the
shortest path i to j with all intermediate vertices in the set {1, 2.......k}.
If k is an intermediate vertex of path p, then we break p down into i → k → j.
Let dij(k) be the weight of the shortest path from vertex i to vertex j with all
intermediate vertices in the set {1, 2.......k}.
A recursive definition is given by
5. the ←1
6. else ←0
7. for k ← 1 to n
8. do for i ← 1 to n
9. do for j ← 1 to n
Johnson's Algorithm
The problem is to find the shortest path between every pair of vertices in a
given weighted directed graph and weight may be negative. Using Johnson's
Algorithm, we can find all pairs shortest path in O (V2 log ? V+VE ) time.
Johnson's Algorithm uses both Dijkstra's Algorithm and Bellman-Ford
Algorithm.
Johnson's Algorithm uses the technique of "reweighting." If all edge weights
w in a graph G = (V, E) are nonnegative, we can find the shortest paths
between all pairs of vertices by running Dijkstra's Algorithm once from each
vertex. If G has negative - weight edges, we compute a new - set of non -
negative edge weights that allows us to use the same method. The new set of
edges weight w must satisfy two essential properties:
For all pair of vertices u, v ∈ V, the shortest path from u to v using weight
function w is also the shortest path from u to v using weight function w.
For all edges (u, v), the new weight w (u, v) is nonnegative.
Given a weighted, directed graph G = (V, E) with weight function w: E→R
and let h: v→R be any function mapping vertices to a real number.
For each edge (u, v) ∈ E define
Step 1: Take any source vertex's' outside the graph and make distance from's'
to every vertex '0'.
Step 2: Apply Bellman-Ford Algorithm and calculate minimum weight on
each vertex.
Step 3: w (a, b) = w (a, b) + h (a) - h (b)
= -3 + (-1) - (-4)
=0
w (b, a) = w (b, a) + h (b) - h (a)
= 5 + (-4) - (-1)
=2
w (b, c) = w (b, c) + h (b) - h (c)
w (b, c) = 3 + (-4) - (-1)
=0
w (c, a) = w (c, a) + h (c) - h (a)
w (c, a) = 1 + (-1) - (-1)
=1
w (d, c) = w (d, c) + h (d) - h (c)
w (d, c) = 4 + 0 - (-1)
=5
w (d, a) = w (d, a) + h (d) - h (a)
w (d, a) = -1 + 0 - (-1)
=0
w (a, d) = w (a, d) + h (a) - h (d)
w (a, d) = 2 + (-1) - 0 = 1
Step 4: Now all edge weights are positive and now we can apply Dijkstra's
Algorithm on each vertex and make a matrix corresponds to each vertex in a
graph
Case 1: 'a' as a source vertex
Case 2: 'b' as a source vertex
Case 3: 'c' as a source vertex
Let G = (V, E) be a flow network. Let s be the source of the network, and let t
be the sink. A flow in G is a real-valued function f: V x V→R such that the
following properties hold:
The quantity f (u, v), which can be positive or negative, is known as the net
flow from vertex u to vertex v. In the maximum-flow problem, we are given
a flow network G with source s and sink t, and we wish to find a flow of
maximum value from s to t.
The three properties can be described as follows:
1. Capacity Constraint makes sure that the flow through each edge
is not greater than the capacity.
2. Skew Symmetry means that the flow from u to v is the negative
of the flow from v to u.
3. The flow-conservation property says that the total net flow out
of a vertex other than the source or sink is 0. In other words, the
amount of flow into a v is the same as the amount of flow out
of v for every vertex v ∈ V - {s, t}
The value of the flow is the net flow from the source,
A super source s,
A super sink t,
For each si, add edge (s, si) with capacity ∞, and
For each ti,add edge (ti,t) with capacity ∞
Figure shows a multiple sources and sinks flow network and an equivalent
single source and sink flow network.
Residual Networks: The Residual Network consists of an edge that can admit
more net flow. Suppose we have a flow network G = (V, E) with source s and
sink t. Let f be a flow in G, and examine a pair of vertices u, v ∈ V. The
sum of additional net flow we can push from u to v before exceeding the
capacity c (u, v) is the residual capacity of (u, v) given by
When the net flow f (u, v) is negative, the residual capacity cf (u,v) is greater
than the capacity c (u, v).
Example: if c (u, v) = 16 and f (u, v) =16 and f (u, v) = -4, then the residual
capacity cf (u,v) is 20.
Given a flow network G = (V, E) and a flow f, the residual network of G
induced by f is Gf = (V, Ef), where
That is, each edge of the residual network, or residual edge, can admit a
strictly positive net flow.
Augmenting Path: Given a flow network G = (V, E) and a flow f,
an augmenting path p is a simple path from s to t in the residual network Gf.
By the solution of the residual network, each edge (u, v) on an augmenting
path admits some additional positive net flow from u to v without violating
the capacity constraint on the edge.
Let G = (V, E) be a flow network with flow f. The residual capacity of an
augmenting path p is
The residual capacity is the maximal amount of flow that can be pushed
through the augmenting path. If there is an augmenting path, then each edge
on it has a positive capacity. We will use this fact to compute a maximum
flow in a flow network.
Ford-Fulkerson Algorithm
Initially, the flow of value is 0. Find some augmenting Path p and increase
flow f on each edge of p by residual Capacity cf (p). When no augmenting
path exists, flow f is a maximum flow.
FORD-FULKERSON METHOD (G, s, t)
1. Initialize flow f to 0
2. while there exists an augmenting path p
3. do argument flow f along p
4. Return f
FORD-FULKERSON (G, s, t)
1. for each edge (u, v) ∈ E [G]
2. do f [u, v] ← 0
3. f [u, v] ← 0
4. while there exists a path p from s to t in the residual network Gf.
5. do cf (p)←min?{ Cf (u,v):(u,v)is on p}
6. for each edge (u, v) in p
7. do f [u, v] ← f [u, v] + cf (p)
8. f [u, v] ←-f[u,v]
Example
Each Directed Edge is labeled with capacity. Use the Ford-Fulkerson
algorithm to find the maximum flow.
Solution
The left side of each part shows the residual network Gf with a shaded
augmenting path p,and the right side of each part shows the net flow f.
Maximum Bipartite Matching
A Bipartite Graph is a graph whose vertices can be divided into two
independent sets L and R such that every edge (u, v) either connect a vertex
from L to R or a vertex from R to L. In other words, for every edge (u, v)
either u ∈ L and v ∈ L. We can also say that no edge exists that connect
vertices of the same set.
Sorting Networks
Comparison Networks
A comparison network is made of wires and comparators. A comparator is a
device with two inputs, x and y, and two outputs, x' and y,' where
x'= min (x, y)
y' = max (x, y)
In Comparison Networks input appear on the left and outputs on the right,
with the smallest input value appearing on the top output and the largest input
value appearing on the bottom output. Each comparator operates in O (1)
time. In other words, we consider that the time between the appearance of the
input values x and y and the production of the output values x' and y' is a
constant.
A wire transmits a value from place to place. A comparison network contains
n input wires a1,a2,........an through which the benefits to be sorted enter the
network, and n output wires b1,b2,......bn which produce the results computed
by the network.
Merging Network
Merging Network is the network that can join two sorted input sequences into
one sorted output sequence. We adapt BITONIC-SORTER [n] to create the
merging network MERGER [n].
The merging network is based on the following assumption:
Given two sorted sequences, if we reverse the order of the second sequence
and then connect the two sequences, the resulting sequence is bitonic.
Example: Given two sorted zero-one sequences X = 00000111 and Y
=00001111, we reverse Y to get YR = 11110000. Concatenating X and
YR yield 0000011111110000, which is bitonic.
The sorting network SORTER [n] need the merging network to implement a
parallel version of merge sort. The first stage of SORTER [n] consists of n/2
copies of MERGER [2] that work in parallel to merge pairs of a 1-element
sequence to produce a sorted sequence of length 2. The second stage subsists
of n/4 copies of MERGER [4] that merge pairs of these 2-element sorted
sequences to generate sorted sequences of length 4. In general, for k = 1, 2.....
log n, stage k consists of n/2k copies of MERGER [2k] that merge pairs of the
2k-1 element sorted sequence to produce a sorted sequence of length2k. At the
last stage, one sorted sequence consisting of all the input values is produced.
This sorting network can be shown by induction to sort zero-one sequences,
and therefore by the zero-one principle, it can sort arbitrary values.
The recurrence given the depth of SORTER [n]
Complexity Theory
Complexity Classes
NP Class Problem
The set of all decision-based problems came into the division of NP Problems
who can't be solved or produced an output within polynomial time but
verified in the polynomial time. NP class contains P class as a subset. NP
problems being hard to solve.
Note: - The term "NP" does not mean "not polynomial." Originally, the term
meant "non-deterministic polynomial. It means according to the one input
number of output will be produced.
P Class Problem
The set of decision-based problems come into the division of P Problems
who can be solved or produced an output within polynomial time. P problems
being easy to solve
Polynomial Time
If we produce an output according to the given input within a specific amount
of time such as within a minute, hours. This is known as Polynomial time.
Non-Polynomial Time
If we produce an output according to the given input but there are no time
constraints is known as Non-Polynomial time. But yes output will produce
but time is not fixed yet.
Decision Based Problem
A problem is called a decision problem if its output is a simple "yes" or "no"
(or you may need this of this as true/false, 0/1, accept/reject.) We will phrase
many optimization problems as decision problems. For example, Greedy
method, D.P., given a graph G= (V, E) if there exists any Hamiltonian cycle.
Np-Hard Class
Here you to satisfy the following points to come into the division of NP-hard
NP-Complete Class
A problem is in NP-complete, if
1. It is in NP
2. It is NP-hard
Pictorial representation of all NP classes which includes NP, NP-hard, and
NP-complete
P contains in NP
P=NP
Reductions
The class NP-complete (NPC) problems consist of a set of decision problems
(a subset of class NP) that no one knows how to solve efficiently. But if there
were a polynomial solution for even a single NP-complete problem, then
every problem in NPC will be solvable in polynomial time. For this, we need
the concept of reductions.
Suppose there are two problems, A and B. You know that it is impossible to
solve problem A in polynomial time. You want to prove that B cannot be
explained in polynomial time. We want to show that (A ∉ P) => (B ∉ P)
Consider an example to illustrate reduction: The following problem is well-
known to be NPC:
3-color: Given a graph G, can each of its vertices be labeled with one of 3
different colors such that two adjacent vertices do not have the same label
(color).
Coloring arises in various partitioning issues where there is a constraint that
two objects cannot be assigned to the same set of partitions. The phrase
"coloring" comes from the original application which was in map drawing.
Two countries that contribute a common border should be colored with
different colors.
It is well known that planar graphs can be colored (maps) with four colors.
There exists a polynomial time algorithm for this. But deciding whether this
can be done with 3 colors is hard, and there is no polynomial time algorithm
for it.
Example of 3-colorable and non-3-colorable graphs
Polynomial Time Reduction
We say that Decision Problem L1 is Polynomial time Reducible to decision
Problem L2 (L1≤p L2) if there is a polynomial time computation function f
such that of all x, xϵL1 if and only if xϵL2.
NP-Completeness
A decision problem L is NP-Hard if
L' ≤p L for all L' ϵ NP.
L is NP-complete if
1. L ϵ NP and
2. L' ≤ p L for some known NP-complete problem L.' Given this
formal definition, the complexity classes are:
1. L ϵ NP and
2. L is NP-hard
If any NP-complete problem is solvable in polynomial time, then every NP-
Complete problem is also solvable in polynomial time. Conversely, if we can
prove that any NP-Complete problem cannot be solved in polynomial time,
every NP-Complete problem cannot be solvable in polynomial time.
Reductions
Concept
If the solution of NPC problem does not exist then the conversion from one
NPC problem to another NPC problem within the polynomial time. For this,
you need the concept of reduction. If a solution of the one NPC problem
exists within the polynomial time, then the rest of the problem can also give
the solution in polynomial time (but it's hard to believe). For this, you need
the concept of reduction.
Example
Suppose there are two problems, A and B. You know that it is impossible to
solve problem A in polynomial time. You want to prove that B cannot be
solved in polynomial time. So you can convert the problem A into
problem B in polynomial time.
Example of NP-Complete Problem
NP problem: - Suppose a DECISION-BASED problem is provided in which
a set of inputs/high inputs you can get high output.
Criteria to come either in NP-hard or NP-complete.
1. The point to be noted here, the output is already given, and you
can verify the output/solution within the polynomial time but
can't produce an output/solution in polynomial time.
2. Here we need the concept of reduction because when you can't
produce an output of the problem according to the given input
then in case you have to use an emphasis on the concept of
reduction in which you can convert one problem into another
problem.
Note 1:- If you satisfy both points then your problem comes into the category
of NP-complete class
Note 2:- If you satisfy the only 2nd points then your problem comes into the
category of NP-hard class
So according to the given decision-based NP problem, you can decide in the
form of yes or no. If, yes then you have to do verify and convert into another
problem via reduction concept. If you are being performed, both then
decision-based NP problems are in NP compete.
Here we will emphasize NPC.
Circuit Satisfiability
According to given decision-based NP problem, you can design the circuit
and verify a given mentioned output also within the P time. The circuit is
provided below:-
Note:- You can design a circuit and verified the mentioned output within
Polynomial time but remember you can never predict the number of gates
which produces the high output against the set of inputs/high inputs within a
polynomial time. So you verified the production and conversion had been
done within polynomial time. So you are in NPC.
SAT (Satisfiability)
A Boolean function is said to be SAT if the output for the given value of the
input is true/high/1
F=X+YZ (Created a Boolean function by CIRCUIT SAT)
These points you have to be performed for NPC
CONCEPTS OF SAT
CIRCUIT SAT≤ρ SAT
SAT≤ρ CIRCUIT SAT
SAT ϵ NPC
Proof of NPC: Reduction has been successfully made within the polynomial
time from CIRCUIT SAT TO SAT. Output has also been verified within the
polynomial time as you did in the above conversation.
So concluded that SAT ϵ NPC.
3CNF Satisfiability
Concept: - In 3CNF SAT, you have at least 3 clauses, and in clauses, you will
have almost 3 literals or constants
Such as (X+Y+Z) (X+Y+Z) (X+Y+Z)
You can define as (XvYvZ) ᶺ (XvYvZ) ᶺ (XvYvZ)
V=OR operator
^ =AND operator
These all the following points need to be considered in 3CNF SAT.
To prove: -
If you can achieve these two points that means 3CNF SAT also in NPC
Clique
To Prove: - Clique is an NPC or not?
For this you have to satisfy the following below-mentioned points: -
Clique
3CNF ≤ρ Clique
Clique ≤ρ 3CNF≤SAT
Clique ϵ NP
1) Clique
Definition: - vertex, and the number of vertices in the Clique represents the
Size of Clique.
CLIQUE COVER: - Given a graph G and an integer k, can we find k subsets
of verticesV1, V2...VK, such that UiVi = V, and that each Vi is a clique of G.
The following figure shows a graph that has a clique cover of size 3.
2) 3CNF ≤ρ Clique
For the successful conversion from 3CNF to Clique, you have to follow the
two steps:-
Draw the clause in the form of vertices, and each vertex represents the literals
of the clauses.
3) Clique ≤ρ 3CNF
As you know that a function of K clause, there must exist a Clique of size k.
It means that P variables which are from the different clauses can assign the
same value (say it is 1). By using these values of all the variables of the
CLIQUES, you can make the value of each clause in the function is equal to
1
Example
You have a Boolean function in 3CNF:-
(X+Y+Z) (X+Y+Z') (X+Y'+Z)
After Reduction/Conversion from 3CNF to CLIQUE, you will get P variables
such as: - x +y=1, x +z=1 and x=1
Put the value of P variables in equation (i)
(1+1+0)(1+0+0)(1+0+1)
(1)(1)(1)=1 output verified
4) Clique ϵ NP
As you know very well, you can get the Clique through 3CNF and to convert
the decision-based NP problem into 3CNF you have to first convert into SAT
and SAT comes from NP.
So, concluded that CLIQUE belongs to NP.
Proof of NPC
Vertex Cover
1) Vertex Cover
It represents a set of vertex or node in a graph G (V, E), which gives the
connectivity of a complete graph
According to the graph G of vertex cover which you have created, the size of
Vertex Cover =2
Subset Cover
1) Subset Cover
Number of a subset of edges after making the union for a get all the edges of
the complete graph G, and that is called Subset Cover.
According to the graph G, which you have created the size of Subset
Cover=2
Vertex Cover
A Vertex Cover of a graph G is a set of vertices such that each edge in G is
incident to at least one of these vertices.
The decision vertex-cover problem was proven NPC. Now, we want to solve
the optimal version of the vertex cover problem, i.e., we want to find a
minimum size vertex cover of a given graph. We call such vertex cover an
optimal vertex cover C*.
An approximate algorithm for vertex cover:
Approx-Vertex-Cover (G = (V, E))
{
C = empty-set;
E'= E;
While E' is not empty do
{
Let (u, v) be any edge in E': (*)
Add u and v to C;
Remove from E' all edges incident to
u or v;
}
Return C;
}
The idea is to take an edge (u, v) one by one, put both vertices to C, and
remove all the edges incident to u or v. We carry on until all edges have been
removed. C is a VC. But how good is C?
VC = {b, c, d, e, f, g}
Traveling-salesman Problem
In the traveling salesman Problem, a salesman must visits n cities. We can
say that salesman wishes to make a tour or Hamiltonian cycle, visiting each
city exactly once and finishing at the city he starts from. There is a non-
negative cost c (i, j) to travel from the city i to city j. The goal is to find a tour
of minimum cost. We assume that every two cities are connected. Such
problems are called Traveling-salesman problem (TSP).
We can model the cities as a complete graph of n vertices, where each vertex
represents a city.
It can be shown that TSP is NPC.
If we assume the cost function c satisfies the triangle inequality, then we can
use the following approximate algorithm.
Triangle inequality
Let u, v, w be any three vertices, we have
Intuitively, Approx-TSP first makes a full walk of MST T, which visits each
edge exactly two times. To create a Hamiltonian cycle from the full walk, it
bypasses some vertices (which corresponds to making a shortcut)
String Matching
String Matching Algorithm is also called "String Searching Algorithm." This
is a vital class of string algorithm is declared as "this is the method to find a
place where one is several strings are found within the larger string."
Given a text array, T [1.....n], of n character and a pattern array, P [1......m],
of m characters. The problems are to find an integer s, called valid
shift where 0 ≤ s < n-m and T [s+1......s+m] = P [1......m]. In other words, to
find even if P in T, i.e., where P is a substring of T. The item of P and T are
character drawn from some finite alphabet such as {0, 1} or {A, B .....Z, a,
b..... z}.Given a string T [1......n], the substrings are represented as T [i......j]
for some 0≤i ≤ j≤n-1, the string formed by the characters in T from index i to
index j, inclusive. This process that a string is a substring of itself (take i = 0
and j =m).
The proper substring of string T [1......n] is T [1......j] for some 0<i ≤ j≤n-1.
That is, we must have either i>0 or j < m-1.
Using these descriptions, we can say given any string T [1......n], the
substrings are
T [i.....j] = T [i] T [i +1] T [i+2]......T [j] for some 0≤i ≤ j≤n-1.
And proper substrings are
T [i.....j] = T [i] T [i +1] T [i+2]......T [j] for some 0≤i ≤ j≤n-1.
Note: If i>j, then T [i.....j] is equal to the empty string or null, which has
length zero.
Algorithms used for String Matching
There are different types of method is used to finding the string
The finite automaton starts in state q0 and reads the characters of its input
string one at a time. If the automaton is in state q and reads input character a,
it moves from state q to state δ (q, a). Whenever its current state q is a
member of A, the machine M has accepted the string read so far. An input
that is not allowed is rejected.
A finite automaton M induces a function ∅ called the called the final-state
function, from ∑* to Q such that ∅ (w) is the state M ends up in after
scanning the string w. Thus, M accepts a string w if and only if ∅ (w) ∈ A.
The function f is defined as
∅ ( ∈ )=q0
∅ (wa) = δ (( ∅ (w), a) for w ∈ ∑*,a ∈ ∑)
FINITE- AUTOMATON-MATCHER (T,δ, m),
1. n ← length [T]
2. q ← 0
3. for i ← 1 to n
4. do q ← δ (q, T[i])
5. If q =m
6. then s←i-m
7. print "Pattern occurs with shift s" s
The primary loop structure of FINITE- AUTOMATON-MATCHER implies
that its running time on a text string of length n is O (n).
Computing the Transition Function: The following procedure computes the
transition function δ from given pattern P [1......m]
COMPUTE-TRANSITION-FUNCTION (P, ∑)
1. m ← length [P]
2. for q ← 0 to m
3. do for each character a ∈ ∑*
4. do k ← min (m+1, q+2)
5. repeat k←k-1
6. Until
7. δ(q,a)←k
8. Return δ
Example
Suppose a finite automaton which accepts even number of a's where ∑ = {a,
b, c}
Solution
q0 is the initial state.
Knuth-Morris-Pratt (KMP)Algorithm
Knuth-Morris and Pratt introduce a linear time algorithm for the string
matching problem. A matching time of O (n) is achieved by avoiding
comparison with an element of 'S' that have previously been involved in
comparison with some element of the pattern 'p' to be matched. i.e.,
backtracking on the string 'S' never occurs
Components of KMP Algorithm
1. The Prefix Function (Π): The Prefix Function, Π for a pattern encapsulates
knowledge about how the pattern matches against the shift of itself. This
information can be used to avoid a useless shift of the pattern 'p.' In other
words, this enables avoiding backtracking of the string 'S.'
2. The KMP Matcher: With string 'S,' pattern 'p' and prefix function 'Π' as
inputs, find the occurrence of 'p' in 'S' and returns the number of shifts of 'p'
after which occurrences are found.
The Prefix Function (Π)
Following pseudo code compute the prefix function, Π:
COMPUTE- PREFIX- FUNCTION (P)
1. m ←length [P] //'p' pattern to be matched
2. Π [1] ← 0
3. k ← 0
4. for q ← 2 to m
5. do while k > 0 and P [k + 1] ≠ P [q]
6. do k ← Π [k]
7. If P [k + 1] = P [q]
8. then k← k + 1
9. Π [q] ← k
10. Return Π
Running Time Analysis
In the above pseudo code for calculating the prefix function, the for loop
from step 4 to step 10 runs 'm' times. Step1 to Step3 take constant time.
Hence the running time of computing prefix function is O (m).
Example
Compute Π for the pattern 'p' below:
Solution
Initially: m = length [p] = 7
Π [1] = 0
k=0
After iteration 6 times, the prefix function computation is complete:
Let us execute the KMP Algorithm to find whether 'P' occurs in 'T.'
For 'p' the prefix function, ? was computed previously and is as follows:
Solution
Initially: n = size of T = 15
m = size of P = 7
Pattern 'P' has been found to complexity occur in a string 'T.' The total
number of shifts that took place for the match to be found is i-m = 13 - 7 = 6
shifts.
Boyer-Moore Algorithm
Robert Boyer and J Strother Moore established it in 1977. The B-M String
search algorithm is a particularly efficient algorithm and has served as a
standard benchmark for string search algorithm ever since.
Robert Stephen Boyer
The B-M algorithm takes a 'backward' approach: the pattern string (P) is
aligned with the start of the text string (T), and then compares the characters
of a pattern from right to left, beginning with rightmost character.
If a character is compared that is not within the pattern, no match can be
found by analyzing any further aspects at this position so the pattern can be
changed entirely past the mismatching character.
For deciding the possible shifts, B-M algorithm uses two preprocessing
strategies simultaneously. Whenever a mismatch occurs, the algorithm
calculates a variation using both approaches and selects the more significant
shift thus, if make use of the most effective strategy for each case.
The two strategies are called heuristics of B - M as they are used to reduce
the search. They are:
Example
If a bad character doesn't exist the pattern then.
Problem in Bad-Character Heuristics
In some cases, Bad-Character Heuristics produces some negative shifts.
Example
COMPUTE-GOOD-SUFFIX-FUNCTION (P, m)
1. Π ← COMPUTE-PREFIX-FUNCTION (P)
2. P'← reverse (P)
3. Π'← COMPUTE-PREFIX-FUNCTION (P')
4. for j ← 0 to m
5. do ɣ [j] ← m - Π [m]
6. for l ← 1 to m
7. do j ← m - Π' [L]
8. If ɣ [j] > l - Π' [L]
9. then ɣ [j] ← 1 - Π'[L]
10. Return ɣ
BOYER-MOORE-MATCHER (T, P, ∑)
1. n ←length [T]
2. m ←length [P]
3. λ← COMPUTE-LAST-OCCURRENCE-FUNCTION (P, m, ∑ )
4. ɣ← COMPUTE-GOOD-SUFFIX-FUNCTION (P, m)
5. s ←0
6. While s ≤ n - m
7. do j ← m
8. While j > 0 and P [j] = T [s + j]
9. do j ←j-1
10. If j = 0
11. then print "Pattern occurs at shift" s
12. s ← s + ɣ[0]
13. else s ← s + max (ɣ [j], j - λ[T[s+j]])
Complexity Comparison of String Matching Algorithm
Algorithm Preprocessing Matching Time
Time
Naive O (O (n - m + 1)m)
Rabin-Karp O(m) (O (n - m + 1)m)
Finite Automata O(m|∑|) O (n)
Knuth-Morris-Pratt O(m) O (n)
Boyer-Moore O(|∑|) (O ((n - m + 1) + |
∑|))