Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
20 views

Design and Analysis of Algorithms - Assignment #2

Uploaded by

bhattifarhan182
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views

Design and Analysis of Algorithms - Assignment #2

Uploaded by

bhattifarhan182
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

COMSATS University Islamabad

CSC301 – Design and Analysis of Algorithms


Assignment #2
Spring 2024

Submitted by: Munib Ur Rehman Qasim


SP22-BAI-036

Class: BSAI-5A

Submitted to: Sir Tanveer Ahmed

Due date: 8th April 2024


Question #1 (Brute Force Approach):

Design a linear algorithm for computing the value of a polynomial:


p(x) = anxn + an-1xn-1 + ... + a1x + a0

Sol.

The problem is to compute the value of polynomial using a linear algorithm. To solve this, we
can just use a loop and run it for n+1 iterations (where “n” is the degree of the polynomial)
and compute a term per iteration and then add them up.

Pseudo Code:

Algorithm ComputePolynomial(A[0..n], x) {
// Input: An array A[0..n] of coefficients of the polynomial, A[n] being the coefficient
of xn. A real number x corresponding to the variable in the polynomial.
// Output: Value of the polynomial (real number).

result ← 0
for i ← 0 to n
result ← result + A[i] * xi

return result
}

Page | 1
Example:
For input A = [2, 3, 1], x = 2
Corresponding to equation x2 + 3x + 2, where x = 2

First iteration:
result = (0) + 2 * 20 = 2
Second iteration:
result = (2) + 3 * 21 = 8
Third iteration:
result = (8) + 1*22 = 12

Output: 12

Time Complexity:
Taking multiplication as the basic operation:
Let “n” be the degree of the polynomial.
Let T(n) be the time complexity.

T(n) = ∑𝑛0 1 = O(n)

Page | 2
Question #2 (Brute Force Approach):

Design an algorithm using Brute-Force strategy for the following problem:


Problem Statement: Let S be a given sequence, Compute the length of
the longest palindromic subsequence(lps) in S.

Sol.

The problem is to find the length of the longest palindromic subsequence in any given
sequence. To solve this using brute force, we check for every possible subsequence to see if
it’s a palindrome and then pick the longest one and store its length. To achieve this, we take
every possible starting position and every possible ending position (greater than the current
starting position) and check to see if that subsequence is a palindrome. If it is, we store the
length. We only check if a subsequence is a palindrome and update the max length if its length
is greater than the current stored max length, so that we don’t check trivial subsequences.

Pseudo Code:
Algorithm IsPalindrome(str[s..e]) {
// Input: A string str[s..e] of characters, where “s” and “e” are the indices of first and
last character respectively.
// Output: true/false corresponding to if the string is a palindrome or not.

while (s < e) {
if (str[s] != str[e]) then return false
s←s+1
e←e-1
}
return true
}

Page | 3
Algorithm LongestPalindromicSubsequence(str[0..n-1]) {
// Input: A string str[0..n-1] of characters, of length n.
// Output: An Integer >= 1, corresponding to the length of the Longest Palindromic
Subsequence.

maxLength ← 1

for i ← 0 to n-2 {
if ((n-i) <= maxLength) then break

for j ← n-1 downto i+1 {


if ((j - i + 1) <= maxLength) then break

if (IsPalindrome(str[i..j]) then maxLength ← j - i + 1


}
}

return maxLength
}

Page | 4
Example:
Given a string str = “azxzyb”
n=6
Below is a representation of how the algorithm runs.
i j subsequence isPalindrome maxLength
0 5 azxzyb False 1
4 azxzy False 1
3 azxz False 1
2 azx False 1
1 az False 1

1 5 zxzyb False 1
4 zxzy False 1
3 zxz True 3
2 Inner loop Breaks

2 5 xzyb False 3
4 Inner loop Breaks

3 Outer loop Breaks

The loops broke as there were no possible subsequences with longer length than the current
maxLength.
Output: 3

Time Complexity:
Taking comparison as the basic operation in IsPalindrome algorithm.
In the worst case isPalindrome takes O(n) time.

Let T(n) be the time complexity for a string of any length “n”.
In the worst case the other 2 loops take O(n2) time, so total complexity will be O(n3):

T(n) = ∑𝑛−1 𝑛−1 3


𝑖=0 ∑𝑗=𝑖+1 𝑛 = O(n )

Page | 5
Question #3 (Brute Force Approach):

Design an algorithm using Brute-Force strategy for the following problem:


Problem Statement: Let I be an m1xm2 size image, and P be sub image of
n1xn2. Find all occurrences of P inside I.

a a a a b a b a b b
P = b b a a a a a b b b
a a b I= b b b a a a b
a a a b b a a
b b a a a b b
a a b a b a a

Sol.
The problem is to find all the occurrences of a sub image P of size n1xn2 inside an image I of
size m1xm2. To solve this problem using brute force approach, we can check each possible sub
image of size n1xn2 inside “I” and check if it matches P, if it does, we can store that occurrence
using the “x” and “y” indices of the top left element of that matched sub image.
For example, for the example in the question, we will store (1, 1) for the blue occurrence, (3,
0) for the purple occurrence, and (2, 3) for the orange occurrence, showing the starting
position of the occurrence. So, output would be an array of these starting positions of the
occurrences. Since we have the size of the pattern, we can find the full pattern by just knowing
the starting position of the occurrence.

Page | 6
Pseudo Code:
Algorithm IsOccurrence(subImage[0..n1-1, 0..n2-1], pattern[0..n1-1, 0..n2-1]) {
// Input: Two n1xn2 matrices
// Output: true/false depending upon if the matrices are equivalent

for i ← 0 to n1-1 {
for j ← 0 to n2-1 {
if (subImage[i, j] != pattern[i, j]) then return false
}
}
return true
}

Algorithm FindOccurrences(image[0..m1-1, 0..m2-1], pattern[0..n1-1, 0..n2-1]) {


// Input: Two matrices of m1xm2 and n1xn2 sizes respectively.
// Output: An array of pairs (x, y) where x and y are integers >= 0

occurrences[] // An array of pairs (x, y)

for i ← 0 to m1 - n1 {
for j ← 0 to m2 - n2 {
if (IsOccurrence(image[i..i+n1-1, j..j+n2-1], pattern)) then
occurrences.push((i, j))
}
}

return occurrences
}

Page | 7
Example:
a a b a
b b a b
b a Image=
Pattern= b a b a
a b
a a b a

For the above pattern and image, the algorithm will check the occurrences in the following
way (left to right, top to bottom):

a a b a a a b a a a b a a a b a
b b a b b b a b b b a b b b a b
b a b a b a b a b a b a b a b a
a a b a a a b a a a b a a a b a

a a b a a a b a a a b a a a b a
b b a b b b a b b b a b b b a b
b a b a b a b a b a b a b a b a
a a b a a a b a a a b a a a b a

a a b a
b b a b
b a b a
a a b a

The blue ones are the matches.


So, the output for this will be:
Output: [(0, 2), (1, 1)]

Page | 8
Time Complexity:

Time complexity for the IsOccurrence algorithm is O(n1* n2) in the worst case since 1st loop
runs n1 times and 2nd loop runs n2 times.

The loops in the FindOccurrences algorithm run (m1 - n1 + 1) and (m2 - n2 + 1) times
respectively. Using product rule, their time complexity will be:

= (m1 - n1 + 1) * (m2 - n2 + 1)
Since m1 is greater than or equal to n1 and m2 is greater than or equal to n2, the highest
term will be m1*m2, so its time complexity will be O(m1*m2)

So, overall time complexity will be (using product rule):


O(m1* m2 * n1* n2)

Page | 9
Question #4 (Brute Force Approach):

Suppose you are working on a moving recommendation system. You are given
movie ratings by multiple reviewers where each row represents a different
reviewer, and each column represents a different movie. Design an algorithm
that determines the number of ratings above the value of the given parameter
x.

Sol.

The problem is to find the number of ratings above a value “x”, in a mxn matrix of ratings. So,
to solve this problem using brute force we just loop through the entire matrix while
comparing each value to “x” and if its greater, then we update our counter variable.

Pseudo Code:

Algorithm CountRatingsAboveThreshold(A[0..m-1, 0..n-1], x) {


// Input: A matrix of size mxn, and a real number “x”
// Output: A nonnegative integer

count ← 0
for i ← 0 to m-1 {
for j ← 0 to n-1 {
if (A[i, j] > x) then count ← count + 1
}
}

return count
}

Page | 10
Example:

3 1 5
A[m, n] = 7 2 9

x=4

Given a matrix of ratings like above, the algorithm will compare elements in the following way
and update the count if necessary.

3 1 5 3 1 5 3 1 5
7 2 9 7 2 9 7 2 9

3 1 5 3 1 5 3 1 5
7 2 9 7 2 9 7 2 9

Blue ones are the places where the count will be updated.
So,

Output: 3

Time Complexity:

As the loops run for m and n iterations respectively, the time complexity will be O(m*n)

Page | 11
Question #5 (Brute Force Approach):

Developing data science applications requires data which often come naturally
in the form of a table, e.g., spreadsheet, which needs a two-dimensional array.
Let D be the dataset for your application. D has some missing values in some
rows or columns. You are performing the preprocessing step before building
the model. Design an algorithm using Brute-Force strategy that replaces the
missing data with average row in which that data is missing.

Sol.

The problem is that given a matrix (2D array), fill out all the null (missing) values with the
average of the corresponding row. To solve this problem using brute force, we can just loop
through the matrix and when we encounter a null value, we find the average of that row and
then replace the null value with the average value. Since replacing a null value with the
average won’t change the average of the row, we can optimize the above approach a little,
so that we don’t recompute the average of a row if there are multiple null values. While
looping through a row we can start summing the values, and when we encounter a null value,
we can sum the rest of the row, find the average, and then go back to the null value, fill the
null value and continue looping through the row starting after that null value we just filled,
then if we encounter more null values in the same row, we don’t have to recompute the
average and can directly fill it.
Another option would be that while looping through a row, we sum the values like in the
previous approach but upon encountering a null value, we store the index of it in a separate
array and continue looping though that row until the end of that row. Then we compute the
average and loop through the array holding the indices of the null values and fill each of them
with the average. In this approach, although it takes some more space, we won’t have to
reiterate over the filled values after the first null value like in the previous approach.

So, we will be choosing the latter approach as it seems to be more efficient in time usage.

Page | 12
Pseudo Code:
Algorithm FillMissingValuesWithAverage(A[0..m-1, 0..n-1]) {
// Input: A matrix A[m, n] of size mxn of real numbers with possible missing values
marked as null (Assuming there is at least one non-null value per row)
// Output: A matrix A[m, n] with null values filled with row average

for i ← 0 to m-1 {
sum ← 0
nullValueIndices[] ← []
nullValueCount ← 0

for j ← 0 to n-1 {
if (A[i, j] = null) then
nullValueCount ← nullValueCount + 1
nullValueIndices.push(j)
else
sum ← sum + A[i, j]
}

if (nullValueCount > 0) then


avg ← sum / (n - nullValueCount)

for j ← 0 to nullValueCount - 1 {
A[i, j] ← avg
}
}

return A
}

Page | 13
Example:

3 ? 5
A[m, n] = 1 3 ?
? 2 6

Null values represented as “?” here.

The algorithm will work as follows:

For i = 0:

First inner loop:

3 ? 5 3 ? 5 3 ? 5
1 3 ? 1 3 ? 1 3 ?
? 2 6 ? 2 6 ? 2 6

sum = 3 sum = 3 sum = 8


nullValueCount = 0 nullValueCount = 1 nullValueCount = 1
nullValueIndices = [] nullValueIndices = [1] nullValueIndices = [1]

Second inner loop:


avg = 8 / (3-1) = 4

3 ? 5 3 4 5
1 3 ? 1 3 ?
To
? 2 6 ? 2 6

(average remains the same after filling, incase there were more null values)

Page | 14
Similarly, after i = 1 iteration:

3 4 5
1 3 2
? 2 6

And after i = 2 iteration:

3 4 5
1 3 2
4 2 6

Output:

3 4 5
1 3 2
4 2 6

Time Complexity:
The outer loop runs for m iterations and first inner loop runs for n iterations. Assuming the
worst case where there is only 1 non-null value, or all values are null, then the 2nd inner loop
will also run for n iterations.

So, the time complexity will be m*(2*n) = O(m*n)

Page | 15
Question #6 (Brute Force Approach):

One can modify selection sort by finding minimum and maximum simultaneity
and swapping them with first and last element in the array. Design an
algorithm that employs the above idea to modify the Selection sort algorithm.
Express your algorithm in Pseudo code.

Sol.

The problem is to modify the selection sort algorithm so that it finds minimum and maximum
item simultaneously and swaps them with first and last element respectively, instead of only
finding minimum and swapping.
To solve this problem, we can find the maximum element along with the minimum element
in the inner loop of selection sort. Also, since we are sorting 2 elements per iteration of the
outer loop, we can reduce the iterations of the outer loop to n/2. Swapping is the tricky part,
since there are a couple of cases we need to consider: min and/or max are in correct positions,
min and max are in each other’s correct positions, one of min or max is in other's correct
position and the default one where they aren’t in correct positions and their
positions/swapping do not affect each other. Before swapping, we can check if value at min
and value at max is the same, if it is then the array is already sorted, and we can exit out of
the loop.
Let x be the position where min needs to be and let y be the position where max needs to be.
To solve the swapping problem, first we can check if min is not in correct position i.e. (min !=
x), if it’s not then we swap it.
Next, we check if max was in min’s correct position i.e. (x), if it was, we update max = min
(since, as we swapped min with x, and max was at x, after swap max will be at min)
Next, we just check if max is not in correct position i.e. (y), we swap element at max with y.
This covers all the cases mentioned above.

Page | 16
Pseudo Code:
Algorithm UpdatedSelectionSort(A[0..n-1]) {
// Input: An array A[0..n-1] of n elements
// Output: A[0..n-1] sorted in increasing order

for i ← 0 to ⌊n/2⌋ − 1 {
min ← i
max ← i
for j ← i + 1 to n - i – 1 {
if (A[j] < A[min]) then min ← j
else if (A[j] > A[max]) then max ← j
}

minVal ← A[min]
maxVal ← A[max]

if (minVal = maxVal) then break

if (min != i) then
A[min] ← A[i]
A[i] ← minVal

if (max = i) then max ← min

if (max != n - i - 1) then
A[max] ← A[n - i - 1];
A[n - i - 1] ← maxVal;
}
return A
}

Page | 17
Example:

Given an array A = [7, 3, 8, 5, 4]


n=5

In first iteration of outer loop (i=0):


A = [7, 3, 8, 5, 4]
min = 1, max = 2, minVal = 3, maxVal = 8

• Swap(min, i): A = [3, 7, 8, 5, 4]


• Swap(max, n-i-1): A = [3, 7, 4, 5, 8]
In second iteration of outer loop (i=1):
A = [3, 7, 4, 5, 8]
min = 2, max = 1, minVal = 4, maxVal = 7

• Swap(min, i): A = [3, 4, 7, 5, 8]


• (max == i) update max: max = min = 2
• Swap(max, n-i-1): A = [3, 4, 5, 7, 8]

Output: [3, 4, 5, 7, 8]

Time Complexity:

Using summations to compute time complexity:


𝑛 𝑛 𝑛
−1 𝑛−𝑖−1 −1 −1 𝒏 𝒏
2 2 2
𝑛 2𝑛 (𝟐 − 𝟏) (𝟐 )
∑ ∑ 2 = ∑ 2(𝑛 − 2𝑖 − 1) = 2𝑛 ( ) − 4 ∑ 𝑖 − = 𝒏𝟐 − 𝒏 −
2 2 𝟐
𝑖=0 𝑖+1 𝑖=0 𝑖=0

= O(n2)

Page | 18
Question #7 (Brute Force Approach):

Bubble sort makes no exchanges on its pass through a list, e.g., the algorithm
makes no exchanges on its second, third and fourth passes for the input [5,2
,7,9,11] because list is sorted in its first pass. In such a scenario algorithm
should stop. Modify Bubble sort (above algorithm) that incorporates this
improvement. {Write a pseudo code of the modified version}.

Sol.
The problem is to modify bubble sort algorithm such that it stops when the list is sorted. To
solve this problem, we can just use a Boolean variable to check if it made any swaps for a pass
through the list. If no swaps were made, we would just exit the loop.

Pseudo Code:
Algorithm UpdatedBubbleSort(A[0..n-1]) {
// Input: An array A[0..n-1] of n elements
// Output: A[0..n-1] sorted in increasing order

for i ← 0 to n - 2 {
swapped ← false
for j ← 0 to n - i - 2 {
if (A[j] > A[j+1]) then
Swap(A[j], A[j+1])
swapped ← true
}
if (!swapped) then break
}
return A
}

Page | 19
Example:
Works just like normal bubble sort (bubbles the max elements to the ends of the array) except
that it stops when the array is sorted. i.e when no swaps are made for a pass through the
array.

3 1 5 2 4

First pass:

1 3 2 4 5

3 swaps

Second pass:

1 2 3 4 5

1 swap

Third pass:

1 2 3 4 5

0 swaps
Loop breaks.

Output:

1 2 3 4 5

Time Complexity:
In the worst case, where the list is sorted in descending order, the complexity is same as
normal bubble sort.
𝑛−2 𝑛−𝑖−2 𝑛−2 𝒏−𝟐

∑ ∑ 1 = ∑ 𝑛 − 𝑖 − 1 = 𝒏(𝒏 − 𝟏) − ∑ 𝒊 − (𝒏 − 𝟏)
𝑖=0 𝑗=0 𝑖=0 𝒊=𝟎

= O(n2)

Page | 20
Question #8 (Brute Force Approach):

Another idea for enhancing the performance of Bubble sort is to sort the list
in both direction as follows:
“Compares each adjacent pair of items in a list in turn, swapping them if
necessary, and alternately passes through the list from the beginning to the
end then from the end to the beginning. It stops when a pass does no swaps”.
Design new algorithm that incorporates above idea. Express the above idea in
pseudo code.

Sol.

The problem is to modify bubble sort so that it first does normal element swapping from start
to end, and then does the same thing from end to start of the list, alternately. Referring to
this as forward and backward pass, in the forward pass the max element will be sorted at the
end of the list and in the backward pass, min element will be sorted at the start of the list and
so on. Another point is that the algorithm should stop when no swaps are made in a pass. To
solve this problem, we can just run the main loop for half iterations and in it perform both a
forward pass and a backward pass one after another. We can conditionally check if there were
no swaps made, so that we break out of the loop. Further we can adjust the conditions for
the inner while loops, so they don’t make any unnecessary iterations (that they only loop up
to the unsorted part).

Page | 21
Pseudo Code:
Algorithm UpdatedBubbleSort(A[0..n-1]) {
// Input: An array A[0..n-1] of n elements
// Output: A[0..n-1] sorted in increasing order

for i ← 0 to ⌊n/2⌋ − 1 {
swapped ← false

for j ← i to n - i - 2 {
if (A[j] > A[j+1]) then
Swap(A[j], A[j+1])
swapped ← true
}

if (!swapped) then break

swapped ← false

for j ← n - i - 2 to i + 1 {
if (A[j] < A[j-1]) then
Swap(A[j], A[j-1])
swapped ← true
}

if (!swapped) then break


}

return A
}

Page | 22
Example:

n=6 A= 3 2 6 5 4 1

For a given list like above, the algorithm will work in the following way:
(for both forward and backward pass, the loop runs from the start of unsorted part up to 1
element before the sorted part)
For first iteration of outer loop (i=0):
After forward pass:

2 3 5 4 1 6 Swapped = true

After backward pass:

1 2 3 5 4 6 Swapped = true

For second iteration of outer loop (i=1):


After forward pass:

1 2 3 4 5 6 Swapped = true

After backward pass:

1 2 3 4 5 6 Swapped = false
No Swaps were made, so the loop Breaks.

Output: 1 2 3 4 5 6

Time Complexity:
In the worst case, time complexity will be:
𝑛 𝑛 𝑛
−1 𝑛−𝑖−2 𝑛−𝑖−2 −1 −1
2 2 2

∑( ∑ 1 + ∑ 1) = ∑((𝑛 − 2𝑖 − 1) + (𝑛 − 2𝑖 − 2)) = ∑(2𝑛 − 4𝑖 − 3)


𝑖=0 𝑗=𝑖 𝑗=𝑖+1 𝑖=0 𝑖=0

= O(n2)

Page | 23
Question #9 (Decrease and Conquer):

Design an algorithm by employing Decrease (by a constant factor) and Conquer


for the following problem.
Problem Statement: Let M be a 2D Array of size m1xm2 sorted row and column
wise and k be a key that we want to search in M.
Example:
Input:
𝟏𝟎 𝟐𝟎 𝟑𝟎 𝟒𝟎
𝟏𝟓 𝟐𝟓 𝟑𝟓 𝟒𝟓
M= [ ] and k = 39
𝟐𝟕 𝟐𝟗 𝟑𝟕 𝟒𝟖
𝟑𝟐 𝟑𝟑 𝟑𝟗 𝟓𝟎
Output: 39 found at index [3, 2]

Sol.

The problem is to find a given key in a mxn matrix sorted both row and column wise using
decrease and conquer. To solve this problem, we can use the properties of this special matrix
that given a top element of any column, every element below it will be greater, and every
column to the right of it will also be have greater elements, so lets say we are at a column and
its top element is 20, and we are searching for 15, since its smaller, we can basically disregard
all the elements in that column and all the elements in the columns to the right of it. And we
can move to the column left to it and continue searching. In the other case where the top
value of the column is less than the key value, we can disregard the whole row to the left of
that column. Using these 2 points, if we start from the top right corner (or bottom left), we
can effectively either disregard an entire column or an entire row at each iteration thus
reducing the search space. Starting from the top right, if the key is less, then we move to the
left column in that same row, and if the key is greater, we move to the row below in the same
column. We keep repeating this either until we find the element, or we exceed the bounds of
the matrix.

Page | 24
Pseudo Code:
Algorithm SearchSortedMatrix(A[0..m-1, 0..n-1], key) {
// Input: A matrix of size mxn, sorted row and column wise. And a key to search.
// Output: A pair (x, y) where x is the row and y is the column of the key in the matrix.
It returns (-1, -1) if key is not found.

i←0
j ← n-1

while (i < m && j >= 0) {


if (A[i, j] = key) then return (i, j)

if (A[i, j] > key) then j ← j – 1


else i ← i + 1
}

return (-1, -1)


}

Example:

Let’s take the example in the question:


It will start at top right (blue marks the current cell, and grey marks disregarded space)

10 20 30 40

A= 15 25 35 45 Key = 39
27 29 37 48 m=4
32 33 39 50 n=4

i = 0, j = 3

Page | 25
After first iteration:

10 20 30 40

15 25 35 45

27 29 37 48

32 33 39 50

i = 0, j = 2
After second iteration:

10 20 30 40

15 25 35 45

27 29 37 48

32 33 39 50

i = 1, j = 2
After third iteration:

10 20 30 40

15 25 35 45

27 29 37 48

32 33 39 50

i = 2, j = 2

After fourth iteration:

10 20 30 40

15 25 35 45

27 29 37 48

32 33 39 50

i = 3, j = 2

At fifth iteration:
At 5th iteration we find the key and return (3, 2)
Output: (3, 2)

Page | 26
Time Complexity:
As in the worst case where the key isn’t found and the key is such that we move from top
right to bottom left, it will take n + m iterations. Where m is the number of rows and n is the
number of columns.
So, time complexity will be O(n + m)

Page | 27
Question #10 (Decrease and Conquer):

We discussed two Searching Algorithms namely “Linear Search” and “Binary


Search”. Linear Search employs “Brute Force” Approach whereas Binary
Search employ “Decrease (by a constant factor) and conquer” approach.
“Insertion sort” which utilize “Decrease (by one) and Conquer strategy can be
improve as follows:
We observed that the while loop of the Insertion-sort algorithm uses a linear
search to scan (backward) through the sorted sub array A[0..i-1]. If we use
Binary Search instead of linear search, the performance of Insertion sort can
be improved. Design an algorithm (write Pseudo code) for this modification in
Insertion sort.

Sol.
The problem is to modify insertion sort algorithm such that it uses a binary search to find the
correct position, where we need to insert the element, instead of a linear search. To solve this
problem, we can first modify the binary search algorithm such that it returns the index where
we need to place the given key, such that the list remains sorted. So, once we have this
modified binary search algorithm, we can use it to get the index “k” for the point of insertion,
then we move the elements from the end of the sorted part up to “k”, and then insert the
element at “k”. This way we don’t have to compare as many elements as we do in linear
search approach. Although this would improve the performance in the practical sense, but
asymptotically its still the same, as we still need to move the elements over one by one up to
“k” for insertion, which takes linear time, so the complexity will still be O(n2).
To modify the binary search, we don’t need to change the while loop much except 2 things.
First that we find the “mid” at the start of the while loop as opposed to the end. Second, if
we find the exact element we return its index + 1 (we can also return its index, but this saves
1 iteration, so slightly more efficient). The rest of the while loop remains the same.
Outside the while loop, we normally return -1, but here we will check if “x” is greater than the
element at mid (mid will always be the last element it checked in the while loop, so either x
will be placed at mid, or after mid such that the list remains sorted), if it is, we return mid + 1,
as position of “x” will be after mid. Else we just return mid, as if “x” is less than element at
mid, then mid will be its correct position.

Page | 28
Pseudo Code:
Algorithm ModifiedBinarySearch(A[0..n-1], x) {
// Input: An array A[0..n-1] of n elements in increasing order.
// Output: Index of array such that if we insert x at that position, the array remains
sorted.

l ← 0, u ← n – 1, mid ← 0 // temporary place holder


while (l <= u) {
mid ← ⌊(𝑙 + 𝑢)/2⌋
if (A[mid] = x) then return mid + 1
if (A[mid] > x) then u ← mid - 1
else l ← mid + 1
}
if (A[mid] < x) return mid + 1
return mid
}
Algorithm UpdatedInsertionSort(A[0..n-1]) {
// Input: An array A[0..n-1] of n elements
// Output: A[0..n-1] sorted in increasing order

for i ← 1 to n-1 {
x ← A[i]
k ← ModifiedBinarySearch(A[0..i-1], x)
for j ← i - 1 downto k {
A[j+1] ← A[j]
}
A[k] ← x
}
return A
}

Page | 29
Example:
Insertion sort works in similar fashion. The difference is the modified binary search. It works
in the following way:

1 2 4 6 9 n = 5, l = 0, u = 4

let’s say x = 5
1st iteration:
mid = 2

6 9 (after 1st iteration)


u = 4, l = 3
2nd iteration:
mid = 3
(after 2nd iteration)
u = 2, l = 3
loop breaks.

Element at mid is 6, since its not less than x, we return mid i.e. 3
Output: 3

Let’s say x = 2
1st iteration:
mid = 2

1 2 (after 1st iteration)


u = 1, l = 0
2nd iteration:
mid = 0

2 (after 2nd iteration)


u = 1, l = 1

Page | 30
3rd iteration:
mid = 1
element found at mid, so we return mid + 1

Output: 2

Time Complexity:
Time complexity of modified binary search is still O(log(n)), but even with this improvement
in searching, it still takes linear time to move the elements for insertion.

So, overall complexity is still O(n2)

Page | 31
Question #11:
(This question demonstrates you how choice of Data structure in designing an algorithm will affect
the time complexity of algorithm)

Suppose we are given a set S of n real numbers. Let x and y be the largest and
smallest element of S respectively.
Range = x – y

For each representation of S given below, design an algorithm to compute the


range.
[Specify your algorithms in Pseudo code]
1) An unsorted array.
2) A sorted array.
3) An array which is a concatenation of two sorted portions.
4) A sorted singly linked list.
5) A binary search tree.
Compare the above algorithms and comment your observation about their
performance(with respect to time, space, computation).

Sol.

The problem is to find the range of a set of elements provided in different data
structures/order and compute their time complexity and compare.

Range = x - y
Where x and y are the largest and smallest elements in the set respectively.

Page | 32
1. Unsorted Array:

Pseudo Code:

Algorithm Range(A[0..n-1]) {
// Input: An array A[0..n-1] of n elements
// Output: x – y, where x and y are the largest and smallest elements respectively.

min ← A[0]
max ← A[0]

for i ← 1 to n-1 {
if (A[i] < min) then min ← A[i]
else if (A[i] > max) then max ← A[i]
}

return max - min


}

Time Complexity:
Time complexity will be O(n)

Page | 33
2. Sorted Array:

Pseudo Code:

Algorithm Range(A[0..n-1]) {
// Input: An array A[0..n-1] of n elements in sorted order
// Output: x – y, where x and y are the largest and smallest elements respectively.

return |𝐴[0] − 𝐴[𝑛 − 1]|


}

Time Complexity:
Time complexity will be O(1)

Page | 34
3. Array which is a concatenation of two sorted portions:

Pseudo Code:

Algorithm Range(A[0..k..n-1]) {
// Input: An array A[0..k..n-1] of n elements, where [0 to k-1] and [k to n-1] are sorted
portions (in ascending order)
// Output: x – y, where x and y are the largest and smallest elements respectively.

min ← A[0]
max ← A[n-1]

if (A[k] < min) then min ← A[k]


if (A[k-1] > max) then max ← A[k-1]

return max – min


}

Time Complexity:
Time complexity will be O(1)

Page | 35
4. Sorted singly linked list:

Pseudo Code:

Algorithm Range(head) {
// Input: head node of sorted linked list in ascending order (where head has “next”
and “data” properties)
// Output: x – y, where x and y are the largest and smallest values respectively

min ← head.data

while (head.next != null) {


head ← head.next
}

max ← head.data

return max – min


}

Time Complexity:
Time complexity will be O(n)

Page | 36
5. Binary Search Tree:

Pseudo Code:

Algorithm Range(root) {
// Input: root node of the binary search tree (where root has “left”, “right” and “data”
properties)
// Output: x – y, where x and y are the largest and smallest values respectively

node ← root
while (node.left != null) {
node ← node.left
}

min ← node.data

node ← root
while (node.right != null) {
node ← node.right
}

max ← node.data

return max – min


}

Time Complexity:
Assuming it’s a complete binary tree, the complexity will be O(log(n))
In the worst case, it will be O(n)

Page | 37

You might also like