Searching and Sorting Algorithms Complete
Searching and Sorting Algorithms
Searching Algorithms:
- Linear / Sequential Search
- Binary Search
- Hash Table Search
Sorting Algorithms:
- Bubble Sort
- Insertion Sort
- Merge Sort
- QuickSort
numberList = [6, 172, 331, 363, 497, 525, 552, 611, 723, 949]
key = 611
fake = 412Linear Search
def linearSearch(numberList, key):
start = 0
end = len(numberList) - 1
for i in numberList:
if start == end:
print('Not Found')
elif i == key:
print("Found")
return(i)
else:
start += 1
linearSearch(numberList, key)
linearSearch(numberList, fake)Output:
Found
Not FoundBinary Search
Binary Search operates on the basis that the list is already sorted, and it essentially guesses higher lower until it can get the correct value if not it quits
Iterative Version
def binarySearch(numberList, key):
max = len(numberList) -1
min = 0
while min <= max:
mid = (min + max) // 2
if key == numberList[mid]:
print('found')
break
elif key < numberList[mid]:
max = mid -1
else:
min = mid + 1
else:
print('not found')Recursive Version
def binarySearchRecursive(numberList, key):
return binarySearchRecursive2(numberList, key, 0, len(numberList) -1)
def binarySearchRecursive2(numberList, key, min, max):
mid = (min + max) // 2
if numberList[mid] == key:
return 'Found'
elif min > max:
return 'Not Found'
else:
if numberList[mid] < key: # when guess is too low
min = mid + 1
else: #numberList[mid] > key, guess is too high
max = mid - 1
return binarySearchRecursive2(numberList, key, min, max)
print(binarySearchRecursive(numberList, key))
print(binarySearchRecursive(numberList, fake))Output:
Found
Not FoundHash Table Search
The location of each item is determined by a hash function of the item itself. This makes hash table search at the designated location of the item and thus less comparisons.
There are two main collision strategies - linear probing, as well as chaining
def hashValue(value,hashNum):
return value%hashNumHash Table with Linear Probing
When items have the same hash, the later item is simply just shifted to the next available index.
def hashTableLinearProbe(numberList):
hashList = [0 for i in numberList]
for number in numberList:
value = hashValue(number, len(numberList))
if hashList[value] == 0:
hashList[value] = number
else:
while hashList[value] != 0:
value += 1
if value == len(numberList):
value = 0
hashList[value] = number
return(hashList)
print(numberList)
print([hashValue(i,len(numberList)) for i in numberList])
hashTableLinearProbe(numberList)Output:
[6, 172, 331, 363, 497, 525, 552, 611, 723, 949]
[6, 2, 1, 3, 7, 5, 2, 1, 3, 9]Output:
[949, 331, 172, 363, 552, 525, 6, 497, 611, 723]Hash Table with Chaining
Instead of using probing, just have a list of all the values that correspond to each hash
def hashTableChaining(numberList):
hashList = [['Null'] for i in numberList]
for number in numberList:
value = hashValue(number, len(numberList))
hashList[value] = [number] + hashList[value]
return(hashList)
hashTableChaining(numberList)Output:
[['Null'],
[611, 331, 'Null'],
[552, 172, 'Null'],
[723, 363, 'Null'],
['Null'],
[525, 'Null'],
[6, 'Null'],
[497, 'Null'],
['Null'],
[949, 'Null']]Sorting Algorithms
Bubble Sort
Bubble sort works by swapping one item at a time. It compares an item with the next item, and swaps the position of items when the one on the left is higher than that on the right. If the two items in comparison are already sorted, it chooses the higher item and uses that to sort instead. (You can see that with how it sorts the 12, 15, 20)
This ensures that with each round, the far right is has increased number of sorted items. (i.e 1st round, highest item would be on far right, 2nd round, the two highest items would be on the right)
unsorted = [45,12,4,15,20,3]
unsortedLong = [45,12,4,15,20,3,39,5,9,30]
def bubbleSort(unsorted):
lastSorted = len(unsorted) - 1
while lastSorted > 0:
lastExchangeIndex = 0 # Take note: the last exchange index is of each round. Last sorted is the things that are already done
for i in range(lastSorted):
if unsorted[i] > unsorted[i+1]:
unsorted[i], unsorted[i+1] = unsorted[i+1], unsorted[i]
lastExchangeIndex = i
lastSorted = lastExchangeIndex
return unsorted
bubbleSort(unsorted)Output:
[3, 4, 12, 15, 20, 45]FUNCTION BUBBLESORT RETURNS ARRAY
LASTSORTED <- LENTH(ARRAY) -1
WHILE LASTSORTED > 0 DO
LASTEXCHANGE <- 0
FOR i 0 TO LASTSORTED DO
IF ARRAY[i] > ARRAY[i+1]
temp <- ARRAY[i+1]
ARRAY[i+1] <- ARRAY[i]
ARRAY[i] <- temp
LASTEXCHANGE <- i
ENDIF
ENDFOR
LASTSORTED <- LASTEXCHANGE
ENDWHILE
ENDFUNCTION
Insertion Sort
Essentially, everything on the left is sorted. The next item on the right that is unsorted (seen as the red numbers that are brought down) is compared with sorted elements on the left individually, from highest to lowest. This continues until a suitable slot is found to put the item in.
unsorted = [45,12,4,15,20,3]
unsortedLong = [45,12,4,15,20,3,39,5,9,30]
def insertionSort(unsorted):
for index in range(len(unsorted)): # this gets one element at a time, starting with second element
swapItem = unsorted[index] # holds the item to be swapped separately (visualised as shifting down above)
comparison = index # defines where the comparison starts, it goes downwards from here.
while comparison > 0 and swapItem < unsorted[comparison-1]: # i keep forgetting the -1 here
unsorted[comparison] = unsorted[comparison -1] # visualised as green shifting to the right
comparison -= 1 # visualised as the empty space shifting
unsorted[comparison] = swapItem # visualised as the holding data being put back into main array
return unsorted
insertionSort(unsortedLong)Output:
[3, 4, 5, 9, 12, 15, 20, 30, 39, 45]# i actually prefer this version, where i just swap each data but idk if its allowed ?
# does not really look the same as animation ..
def linearSort(unsorted):
for i in range(len(unsorted)):
comparison = i
swapItem = unsorted[i]
while comparison > 0 and swapItem < unsorted[comparison -1]:
unsorted[comparison], unsorted[comparison-1] = unsorted[comparison-1], unsorted[comparison]
comparison -= 1
return unsorted
linearSort(unsorted)Output:
[3, 4, 12, 15, 20, 45]MergeSort
Merge sort essentially continues splitting the original array into arrays until they form arrays of size 2. In the example below, 10 items are split into 5/5 then 3/2 each.
When the subarray is of size 3 - this gets further split into 1, and 2. Take note that this differs from the visualisation below. In the vis, the single item (4) is compared with the first pair (45, 12). In the code below, it is compared with the second pair (15, 20).
After the splitting, comparison occurs, in such order:
- Within the 2 element subarray
- Within the 3 element subarray
- Between the 2 and 3 element subarray, forming a 5 element sorted subarray
- Between the two 5 element subarray, which after comparison forms the full sorted array.
unsorted = [45,12,4,15,20,3]
unsortedLong = [45,12,4,15,20,3,39,5,9,30]
def mergeSort(Array):
if len(Array) > 1: # remember that it is >1, because the minimum length size is 2 in order to do swapping.
# recursive splitting into small groups. the if statement above is important.
mid = len(Array) // 2
left = mergeSort(Array[:mid])
right = mergeSort(Array[mid:])
# here is the comparison
Array = []
while len(left) > 0 and len(right) > 0:
if left[0] < right[0]:
Array.append(left.pop(0))
else:
Array.append(right.pop(0))
# this is in the case where either left or right is empty already,
# since the other side is sorted and more than Array, we can just dump it as such.
Array += left + right
return Array
mergeSort(unsortedLong)Output:
[3, 4, 5, 9, 12, 15, 20, 30, 39, 45]QuickSort
# unsorted = [45,12,4,15,20,3]
unsorted = [45,12,4,15,20,3,39,5,9,30]
def split(Array, low, high):
middle = (low + high) // 2
pivot = Array[middle]
left = low + 1
right = high
# swapping to set the pivot on the left
Array[low], Array[middle] = Array[middle], Array[low]
while left <= right:
# because i want left to be more than right, and i
# want the left pointer to be <= right pointer
while left <= right and Array[left] <= pivot: # the <= IS CRUCIALL
left += 1
while Array[right] > pivot: # bc i want the right to be a value < pivot
right -= 1
if left < right:
Array[left], Array[right] = Array[right], Array[left]
Array[low], Array[right] = Array[right], Array[low] # the swap back
return right
def quickSort(Array, low, high):
if low < high: # i keep forgetting this
pivot = split(Array, low, high)
quickSort(Array, low, pivot -1)
quickSort(Array, pivot + 1, high)
# notice how pivot is not included, because it is 'locked in'
quickSort(unsorted, 0, len(unsorted) -1)
print(unsorted)Output:
[3, 4, 5, 9, 12, 15, 20, 30, 39, 45]