Algorithms 93 Slides Combined
Algorithms 93 Slides Combined
An Introduction to Algorithms
• Definition
• Algorithm Characteristics
• Algorithm Properties
Correctness
Clear and Unambiguous: Algorithm should be clear and unambiguous. Each of its steps should be clear in all
aspects and must lead to only one meaning.
Finite-ness: The algorithm must be finite, i.e. it should not end up in an infinite loops or similar.
o It should solve the problem in a finite time (reasonable as well)
Feasible: The algorithm must be simple, generic and practical, such that it can be executed upon will the
available resources. It must not contain some future technology, or anything.
Language Independent: The Algorithm designed must be language-independent, i.e. it must be just plain
instructions that can be implemented in any language, and yet the output will be same, as expected.
Characteristics of an Algorithm
• Unambiguous − Algorithm should be clear and unambiguous. Each of its steps (or
phases), and their inputs/outputs should be clear and must lead to only one meaning.
• Output − An algorithm should have 1 or more well-defined outputs and should match
the desired output.
Each operation in an algorithm must be doable, that is, the operation must
be something that is possible to do.
Algorithm Phases
• Implement
Writing and coding the algorithm
Algorithm
Phases
• Experiment
Experiment with different variables
Algorithm
Phases
• Sequential operations.
A sequential operation carries out a single well-defined task. When
that task is finished, the algorithm moves on to the next operation.
• Conditional operation.
A conditional operation is the “question-asking” instructions of an algorithm.
It asks a question and then select the next operation to be executed according
to the question answer
• Iterative operations.
An Iterative operation is a “looping” instruction of an algorithm. It tells us not
to go on to the next instruction, but, instead, to go back and repeat the
execution of a pervious block of instructions
Algorithm Analysis and
Design
Method for Developing an
Algorithm
Representing Algorithms
Advantages Disadvantages
Familiar Verbose
Imprecise -> Ambiguity
Rarely used for complex or
technical algorithms
(2) In Terms of Formal
Programming Language
Advantages Disadvantages
Advantages Disadvantages
Solution
1. Get dividend, divisor
2. IF divisor = 0 THEN
1) Display error message “divisor must be non-zero”
2) Exit algorithm
3. Compute quotient = dividend/divisor
4. Display dividend, divisor, quotient
(4) Flowcharts
A flowchart is a graphical representation of an algorithm. Once the
flowchart is drawn, it becomes easy to write the program in any high-level
language.
Flowcharts
for(i = 2; i <= 6; i = i + 2) {
printf("%d\t", i + 1);
X1 = r +
X2 = r -
Efficiency of Algorithms
instruction space
data space
run-time stack space
Analysis of Time Complexity
Running time depends upon:
Compiler used
R/w speed to Memory and Disk
Machine Architecture : 32 bit vs 64
Input size (rate of growth of time)
list_sum (A, n)
{
Cost # of times
Sum = 0 1 1
for i = 1 to n-1 2 n+1
sum = sum + 2 n
A(i) 1 1
return sum
}
T(list_sum) = 4n + 4
RAM model is not realistic:
• Not all memory accesses take the same time (cache, main
memory, disk).
2n O(2n) exponential
n! O(n!) factorial
Steps for finding Big-O runtime:
2n3+7n2+2000=Θ(n3)
Dropping lower order terms is always Ok since there will always be a n 0
after which Θ(n3) has higher values than Θn2) regardless of the constant.
Function Big-O Name
1 O(1) Constant
log n O(log n) logarithmic
n 1/2 O(n 1/2) Square Root
n O(n) linear
n log n O(n log n) n log n
n2 O(n2) quadratic
n3 O(n3) cubic
2n O(2n) exponential
nc O( n c) Polynomial
n! O(n!) factorial
Example 1
Example 2
Example 6
O(n log3 n)
Example 8
1. list1 = [8, 2, 7, 17, 12, 54, 21, 64, 12, 32] def search(arr, x):
2. print('List has the items: ', list1)
3. searchItem = int(input('Enter a number to search for: ')) for i in range(len(arr)):
4. found = False
if arr[i] == x:
5. for i in range(len(list1)): return i
6. if list1[i] == searchItem:
7. found = True return -1
8. print(searchItem, ' was found in the list at index ', i )
9. break
10.if found == False:
11. print(searchItem, ' was not found in the list!')
Binary search
# Function call
while left <= right:
result = binarySearch(arr, 0, len(arr)-1, x)
mid = left + (right - l) // 2;
if result != -1:
if arr[mid] == x:
print ("Element is present at index % d" %
return mid
result)
else:
elif arr[mid] < x:
print ("Element is not present in array")
left = mid + 1
else:
right = mid - 1
The basic idea of insertion sort is that one element from the
input elements is consumed in each iteration to find its correct
position i.e., the position to which it belongs in a sorted array.
It iterates the next element in the array (It compares the current element with the
largest value in the sorted array):
If the current element is greater than the largest value in the sorted array, then it
leaves the element in its place and moves on to the next element.
Else
It finds its correct position in the sorted array and moves it to that position. This is
done by shifting all the elements, which are larger than the current element, in the
sorted array to one position ahead.
Insertion Sort Steps
INSERTION-SORT(A)
for i = 1 to n
key ← A [i]
j←i–1
while j > = 0 and A[j] > key
A[j+1] ← A[j]
j←j–1
End while
A[j+1] ← key
End for
# Insertion Sort-Python
def insertionSort(arr):
# Traverse through 1 to len(arr)
for i in range(1, len(arr)):
key = arr[i]
# Shift array elements greater than the key to the right
#These elements are: arr[0..i-1.
j = i-1
while j >= 0 and key < arr[j] :
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key Complexity of Insertion Sort
arr = [5, 6, 1, 9, 3, 4]
insertionSort(arr)
for i in range (len(arr)):
print ("% d" % arr[i])
Sorting Algorithms
Merge Sort
• In merge sort, we break the given array midway, for example if the original array had
100 elements, then merge sort will break it down into two subarrays with 50 elements
each.
These subarrays are repeatedly broken into smaller subarrays, until
we have multiple subarrays with single element in them.
• An array with a single element is already sorted, so once we break the original array
into subarrays which has only a single element, we have successfully broken down
our problem into base problems.
• Next, all these sorted subarrays should be merged - step by step to form one single
sorted array.
Algorithm Steps
Given arr.
• left start index, right last index.
• find the middle of the array: mid = (left + right ) / 2.
• break the array into two subarrays:
• ( left to mid ) and (mid + 1 ) to right
• Continue the process of breaking into halves until reaching single
elements.
• Merge the subarrays.
Example
Arr = [13, 6, 3, 11, 9, 2, 4, 5, 8]
Merge Sort
• Merge Sort is quite fast and has a time complexity of O(n*log n). It is also a
stable sort, which means the "equal" elements are ordered in the same order
in the sorted list.
• Pivot element can be any element from the array, it can be the first
element, the last element or any random element.
For example:
In the array: {49, 36, 63, 16, 17, 8, 6, 24}, Pivot -24
So after the first pass, the list will be changed like this.
{6 8 17 16 24 63 36 49}
Hence after the first pass, pivot will be set at its position. All the elements smaller than the
pivot will be on the left of it and all the elements larger than the pivot - to the right.
Now 6 8 17 14 and 63 36 49 are considered as two separate subarrays, and same recursive logic
will be applied on them, and we will keep doing this until the complete array is sorted.
uick Sort Functions
def partition(arr, low, high):
i = ( low-1 ) # index of smaller element
# QuickSort main function
pivot = arr[high]
def quickSort(arr, low, high):
for j in range(low , high):
if low < high:
if arr[j] < pivot: # If current element < pivot
pi = partition(arr, low, high)
i = i+1
quickSort(arr, low, pi-1)
arr[i], arr[j] = arr[j], arr[i]
quickSort(arr, pi+1, high)
arr[i+1], arr[high] = arr[high], arr[i+1]
return ( i+1 )
6 0 0 3 9 5 14 15 8 6 2 4 7
1. def partition(arr, low, high): 4 0 1 3 9 5 14 15 8 6 2 4 7
2. i = ( low-1 ) 4 0 2 3 9 5 14 15 8 6 2 4 7
3. pivot = arr[high] T
6,7 1 2 3 5 9 14 15 8 6 2 4 7
4. for j in range(low , high):
4 1 3 3 5 9 14 15 8 6 2 4 7
5. if arr[j] < pivot:
4 1 4 3 5 9 14 15 8 6 2 4 7
6. i = i+1
4 1 5 3 5 9 14 15 8 6 2 4 7
7. arr[i], arr[j] = arr[j], arr[i]
8. arr[i+1], arr[high] = arr[high], arr[i+1] 4 1 6 3 5 9 14 15 8 6 2 4 7
T
9. return ( i+1 ) 6,7 2 6 3 5 6 14 15 8 9 2 4 7
4 2 7 3 5 6 14 15 8 9 2 4 7
T 6,7 3 7 3 5 6 2 15 8 9 14 4 7
4 3 8 3 5 6 2 15 8 9 14 4 7
T 6,7 4 8 3 5 6 2 4 8 9 14 15 7
pi = partition(arr, low, high)
4 4 9 3 5 6 2 4 8 9 14 15 7
8 4 9 3 5 6 2 4 9 14 15 8
5 is returned to the calling st. 7
Complexity Analysis of Quick Sort
• if partitioning leads to almost equal subarrays, then the running time is
the best, with time complexity as O(n*log n).
2) Modify the count array such that each element at each index
stores the sum of previous counts.
Index: 0 1 2 3 4 5 6 7 8 9
Count: 0 2 4 4 5 6 6 7 7 7
Step 2: Update the count array so that element at each index, say i,
is equal to:
Count[i] = count[x] ; 0≤ x ≤i
The updated count array gives the index of each element of array A in the sorted
sequence. Assume that the sorted sequence is stored in an output array, say B, of size n.
1
n-
a. Set i=0 and c = A[i]
i=
i ll
)t
b. Insert c into B[x] ; x = (count[c] – 1) .
(d
o
c. Decrement count[c] by 1
)t
(a
ps
d. Increment i by 1
tes
at
pe
Re
1, 7, 2, 3, 2, 2, 1, 4, 7, 5, 3, 9, 4
Count: 0 2 5 7 9 10 10 12 12 13 Positions of input array elements in the sorted array
Sorted array; 1 1 2 2 2 3 3 4 4 5 7 7 9
counting algortithm - Implementation 2
• We need just the count array
• No need to scan the input array
• The sorted array is generated directly from the
count/frequency of each element by expansion the
occurrences of each element
Index: 0 1 2 3 4 5 6 7 8 9
Count: 0 2 3 2 2 1 0 2 0 1
Sorted array: 1 1 2 2 2 3 3 4 4 5 7 7 9
1, 7, 2, 3, 2, 2, 1, 4, 7, 5, 3, 9, 4
1 1 2 2 2 3 3 4 4 5 7 7 9
# Python Program – counting sort
def count_sort(array, k):
m=k+1
count = [0] * m
for x in array:
# count occurences of numbers
count[x] += 1
i=0
for x in range(m):
for j in range(count[x]):
array1[i] = x
i += 1
return array
print (count_sort( [1, 2, 5, 3, 2, 1, 4, 8,2, 3, 2, 1], 8 ))
Time Complexity: O(n+k) where n is the number of elements in
input array and k is the range of input.
for i = 1 to n - 1
/* set current element as minimum*/
min = i selectionSort(array, size)
repeat (size - 1) times
/* check the element to be minimum */ set the first unsorted element as the minimum
for each of the unsorted elements
for j = i+1 to n if element < currentMinimum
if list[j] < list[min] then set element as new minimum
min = j; swap minimum with first unsorted position
end if end selectionSort
end for
end procedure
Selection procedure selection sort O( )
sort arr: array of items
n : size of the array
for i = 1 to n - 1
min = i
for j = i+1 to n
if arr[j] < arr[min] then
min = j;
end if
end for
if indexMin != i then
swap arr[min] and arr[i]
end if
end for
end procedure
# Selection sort in Python