DS 5
DS 5
Searching
Searching is the fundamental process of locating a specific element or item within a
collection of data. This collection of data can take various forms, such as arrays, lists,
trees, or other structured representations. Searching plays an important role in various
computational tasks and real-world applications, including information retrieval, data
analysis, decision-making processes, and more. Two popular search methods are Linear
Search and Binary Search.
Sequential Searching
Linear search is also called as sequential search algorithm. It is the simplest
searching algorithm.
In Linear search, we simply traverse the list completely and match each element of
the list with the item whose location is to be found. If the match is found, then the
location of the item is returned; otherwise, the algorithm returns NULL.
It is widely used to search an element from the unordered list.
The worst-case time complexity of linear search is O(n).
Algorithm
Linear_Search(a, n, val) // 'a' is the given array, 'n' is the size of given array, 'val' is the
value to search
Step 1: set pos = -1
Step 2: set i = 1
Step 3: repeat step 4 while i <= n
Step 4: if a[i] == val
set pos = i
print pos
go to step 6
[end of if]
set i = i + 1
[end of loop]
Step 5: if pos = -1
print "value is not present in the array "
[end of if]
Step 6: exit
Binary Search
Binary search is a search algorithm used to find the position of a target value
within a sorted array.
It follows the divide and conquer approach in which the list is divided into two
halves, and the item is compared with the middle element of the list. If the match is
found then, the location of the middle element is returned. Otherwise, we search into
either of the halves depending upon the result produced through the match.
NOTE: Binary search can be implemented on sorted array elements. If the list
elements are not arranged in a sorted manner, we have first to sort them.
The time complexity of binary search is O(log n), where n is the number of elements
in the array.
Steps:
Divide the search space into two halves by finding the middle index “mid” .
Compare the middle element of the search space with the key.
If the key is found at middle element, the process is terminated.
If the key is not found at middle element, choose which half will be used as the
next search space.
If the key is smaller than the middle element, then the left side is used
for next search.
If the key is larger than the middle element, then the right side is used
for next search.
This process is continued until the key is found or the total search space is
exhausted.
Algorithm
Binary_Search(a, lower_bound, upper_bound, val) // 'a' is the given array, 'lower_bound' is the
index of the first array element, 'upper_bound' is the index of the last array element, 'val' is the
value to search
Step 1: set beg = lower_bound, end = upper_bound, pos = - 1
Step 2: repeat steps 3 and 4 while beg <=end
Step 3: set mid = (beg + end)/2
Step 4: if a[mid] = val
set pos = mid
print pos
go to step 6
else if a[mid] > val
set end = mid - 1
else
set beg = mid + 1
[end of if]
[end of loop]
Step 5: if pos = -1
print "value is not present in the array"
[end of if]
Step 6: exit
Hashing
Hashing is a technique used in data structures to store and retrieve data efficiently. It
involves using a hash function to map data items to a fixed-size array which is called
a hash table.
Hash Table
A hash table is also known as a hash map. It is a data structure that stores key-value pairs.
It uses a hash function to map keys to a fixed-size array, called a hash table. This allows
in faster search, insertion, and deletion operations.
Applications of Hashing:
Selection Sort
In selection sort, the smallest value among the unsorted elements of the array is selected
in every pass and inserted to its appropriate position into the array.
It is an in-place comparison sorting algorithm.
In this algorithm, the array is divided into two parts, first is sorted part, and another one is
the unsorted part.
Initially, the sorted part of the array is empty, and unsorted part is the given array. Sorted
part is placed at the left, while the unsorted part is placed at the right.
In selection sort, the first smallest element is selected from the unsorted array and placed
at the first position. After that second smallest element is selected and placed in the
second position. The process continues until the array is entirely sorted.
The average and worst-case complexity of selection sort is O(n2), where n is the number
of items. Due to this, it is not suitable for large data sets.
Selection sort is generally used when -
o A small array is to be sorted
o Swapping cost doesn't matter
o It is compulsory to check all elements
Algorithm
SELECTION SORT(arr, n)
Step 1: Repeat Steps 2 and 3 for i = 0 to n-1
Step 2: CALL SMALLEST(arr, i, n, pos)
Step 3: SWAP arr[i] with arr[pos]
[END OF LOOP]
Step 4: EXIT
Insertion sort
It is a simple sorting algorithm that works by iteratively inserting each element of
an unsorted list into its correct position in a sorted portion of the list.
It is a stable sorting algorithm, meaning that elements with equal values maintain
their relative order in the sorted output.
Insertion sort is like sorting playing cards in your hands. You split the cards into
two groups: the sorted cards and the unsorted cards. Then, you pick a card from the
unsorted group and put it in the right place in the sorted group.
Insertion sort is a simple sorting algorithm that works by building a sorted array
one element at a time.
It is considered an “in-place” sorting algorithm, meaning it doesn’t require any
additional memory space beyond the original array.
Time Complexity: O(N^2)
Pseudocode
Algorithm: Insertion-Sort(A)
for j = 2 to A.length
key = A[j]
i=j–1
while i > 0 and A[i] > key
A[i + 1] = A[i]
i = i -1
A[i + 1] = key
Divide and conquer is a technique of breaking down the algorithms into subproblems,
then solving the subproblems, and combining the results back together to solve the
original problem.
The key process in quickSort is a partition(). The target of partitions is to place the
pivot (any element can be chosen to be a pivot) at its correct position in the sorted
array and put all smaller elements to the left of the pivot, and all greater elements to
the right of the pivot.
Partition is done recursively on each side of the pivot after the pivot is placed in its
correct position and this finally sorts the array.
Choosing the pivot
Picking a good pivot is necessary for the fast implementation of quicksort. Some of the ways
of choosing a pivot are as follows -
Partition Algorithm:
The logic is simple, we start from the leftmost element and keep track of the index of
smaller (or equal) elements as i. While traversing, if we find a smaller element, we swap
the current element with arr[i]. Otherwise, we ignore the current element.
Advantages of Quick Sort:
It is a divide-and-conquer algorithm that makes it easier to solve problems.
It is efficient on large data sets.
It has a low overhead, as it only requires a small amount of memory to function.
Disadvantages of Quick Sort:
It has a worst-case time complexity of O(N2), which occurs when the pivot is
chosen poorly.
It is not a good choice for small data sets.
It is not a stable sort, meaning that if two elements have the same key, their
relative order will not be preserved in the sorted output in case of quick sort,
because here we are swapping elements according to the pivot’s position
(without considering their original positions).
Merge Sort
Merge sort is similar to the quick sort algorithm as it uses the divide and conquer
approach to sort the elements.
It divides the given list into two equal halves, calls itself for the two halves and then
merges the two sorted halves.
The sub-lists are divided again and again into halves until the list cannot be divided
further. Then we combine the pair of one element lists into two-element lists, sorting
them in the process. The sorted two-element pairs is merged into the four-element
lists, and so on until we get the sorted list.
MergeSort(arr):
1. If the length of the array is 1 or less, return the array as it is already sorted.
2. Divide the array into two halves, let's call them left and right.
3. Recursively call MergeSort on the left half.
4. Recursively call MergeSort on the right half.
5. Merge the two sorted halves:
5.1 Initialize empty arrays to store the merged result.
5.2 Compare the elements from the left and right halves, and add the
smaller (or larger, for descending order) element to the merged array.
5.3 Continue this process until one of the halves is exhausted.
5.4 Add the remaining elements from the non-empty half to the merged array.
6. Return the merged array.
Advantages of Merge Sort:
Stability: Merge sort is a stable sorting algorithm, which means it maintains the
relative order of equal elements in the input array.
Guaranteed worst-case performance: Merge sort has a worst-case time
complexity of O(N logN), which means it performs well even on large datasets.
Simple to implement: The divide-and-conquer approach is straightforward.
Disadvantage of Merge Sort:
Space complexity: Merge sort requires additional memory to store the merged
sub-arrays during the sorting process.
Not in-place: Merge sort is not an in-place sorting algorithm, which means it
requires additional memory to store the sorted data. This can be a disadvantage
in applications where memory usage is a concern.