Skip to content

added my shell sort exercise solution code #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
105 changes: 105 additions & 0 deletions 1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# ### Binary Search Exercise
# 1. When I try to find number 5 in below list using binary search, it doesn't work and returns me -1 index. Why is that?

# ```numbers = [1,4,6,9,10,5,7]```

# This is because the array is not sorted in order from lowest to highest.
# Once it splits the first time, it starts looking in the [1,4,6] range and doesn't find 5

# 1. Find index of all the occurances of a number from sorted list

# ```
# numbers = [1,4,6,9,11,15,15,15,17,21,34,34,56]
# number_to_find = 15
# ```
# This should return 5,6,7 as indices containing number 15 in the array

from util import time_it

@time_it
def linear_search(numbers_list, number_to_find):
for index, element in enumerate(numbers_list):
if element == number_to_find:
return index
return -1

@time_it
def binary_search(numbers_list, number_to_find):
left_index = 0
right_index = len(numbers_list) - 1
mid_index = 0

while left_index <= right_index:
mid_index = (left_index + right_index) // 2
mid_number = numbers_list[mid_index]

if mid_number == number_to_find:
return mid_index

if mid_number < number_to_find:
left_index = mid_index + 1
else:
right_index = mid_index - 1

return -1

def binary_search_recursive(numbers_list, number_to_find, left_index, right_index):
if right_index < left_index:
return -1

mid_index = (left_index + right_index) // 2
if mid_index >= len(numbers_list) or mid_index < 0:
return -1

mid_number = numbers_list[mid_index]

if mid_number == number_to_find:
return mid_index

if mid_number < number_to_find:
left_index = mid_index + 1
else:
right_index = mid_index - 1

return binary_search_recursive(numbers_list, number_to_find, left_index, right_index)

#this should run the binary search, find the index, and then recursively run the search on both the right and left side
def binary_search_multiple(numbers_list, number_to_find):

index = binary_search(numbers_list,number_to_find)
result_indices = [index]

# find all indices on the left
i = index - 1
while i>=0:
if numbers_list[i] == numbers_list[index]:
result_indices.append(i)
else:
break
i = i-1

# find all indices on the right
i = index + 1
while i<len(numbers_list):
if numbers_list[i] == numbers_list[index]:
result_indices.append(i)
else:
break
i = i+1

return sorted(result_indices)

numbers_list = [12, 15, 17, 19, 21, 21, 21, 21, 24, 45, 67]
number_to_find = 21

index = binary_search_multiple(numbers_list, number_to_find)
print(f"Number found at index {index} using binary search")

numbers = [1,4,6,9,11,15,15,15,15,17,21,34,34,56]
number_to_find = 15

index = binary_search_multiple(numbers, number_to_find)
print(f"Number found at index {index} using binary search")

#Lesson: I was approaching it wrong. If something isn't working, scratch the approach.
#Lesson #2: Try the simplest solution first. Although in this case it's a bit ugly since you're just doing a linear search after your binary search
84 changes: 84 additions & 0 deletions 2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# ### Bubble Sort Exercise

# Modify [bubble_sort function](https://github.com/codebasics/data-structures-algorithms-python/blob/master/algorithms/2_BubbleSort/bubble_sort.py) such that it can sort following list of transactions happening in an electronic store,
# ```
# elements = [
# { 'name': 'mona', 'transaction_amount': 1000, 'device': 'iphone-10'},
# { 'name': 'dhaval', 'transaction_amount': 400, 'device': 'google pixel'},
# { 'name': 'kathy', 'transaction_amount': 200, 'device': 'vivo'},
# { 'name': 'aamir', 'transaction_amount': 800, 'device': 'iphone-8'},
# ]
# ```
# bubble_sort function should take key from a transaction record and sort the list as per that key. For example,
# ```
# bubble_sort(elements, key='transaction_amount')
# ```
# This will sort elements by transaction_amount and your sorted list will look like,
# ```
# elements = [
# { 'name': 'kathy', 'transaction_amount': 200, 'device': 'vivo'},
# { 'name': 'dhaval', 'transaction_amount': 400, 'device': 'google pixel'},
# { 'name': 'aamir', 'transaction_amount': 800, 'device': 'iphone-8'},
# { 'name': 'mona', 'transaction_amount': 1000, 'device': 'iphone-10'},
# ]
# ```
# But if you call it like this,
# ```
# bubble_sort(elements, key='name')
# ```
# output will be,
# ```
# elements = [
# { 'name': 'aamir', 'transaction_amount': 800, 'device': 'iphone-8'},
# { 'name': 'dhaval', 'transaction_amount': 400, 'device': 'google pixel'},
# { 'name': 'kathy', 'transaction_amount': 200, 'device': 'vivo'},
# { 'name': 'mona', 'transaction_amount': 1000, 'device': 'iphone-10'},
# ]
# ```

# base bubble_sort. you can use this to sort strings too
def bubble_sort(elements):
size = len(elements)

for i in range(size-1):
swapped = False
for j in range(size-1-i):
if elements[j] > elements[j+1]:
tmp = elements[j]
elements[j] = elements[j+1]
elements[j+1] = tmp
swapped = True

if not swapped:
break

def bubble_sort_by_key(elements, key):
size = len(elements)

for i in range(size-1):
swapped = False
for j in range(size-1-i):
if elements[j][key] > elements[j+1][key]:
tmp = elements[j]
elements[j] = elements[j+1]
elements[j+1] = tmp
swapped = True

if not swapped:
break


elements = [5,9,2,1,67,34,88,34]
elements = [1,2,3,4,2]
elements = ["mona", "dhaval", "aamir", "tina", "chang"]

bubble_sort(elements)
print(elements)

elements2 = [ { 'name': 'kathy', 'transaction_amount': 200, 'device': 'vivo'},
{ 'name': 'dhaval', 'transaction_amount': 400, 'device': 'google pixel'},
{ 'name': 'aamir', 'transaction_amount': 800, 'device': 'iphone-8'},
{ 'name': 'mona', 'transaction_amount': 1000, 'device': 'iphone-10'},
]
bubble_sort_by_key(elements2,key='transaction_amount')
print(elements2)
66 changes: 66 additions & 0 deletions 3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
def swap(a, b, arr):
if a!=b:
tmp = arr[a]
arr[a] = arr[b]
arr[b] = tmp

# Sorts a (portion of an) array, divides it into partitions, then sorts those
def quicksort(A, lo, hi):
if lo >= 0 and lo < hi:
lt, gt = partition(A, lo, hi) # Multiple return values
quicksort(A, lo, lt - 1)
quicksort(A, gt + 1, hi)

# Divides array into three partitions
def partition(A, lo, hi):
# Pivot value
pivot = A[(lo + hi) // 2] # Choose the middle element as the pivot (integer division)

# Lesser, equal and greater index
lt = lo
eq = lo
gt = hi

# Iterate and compare all elements with the pivot

while eq <= gt:
if A[eq] < pivot:
# Swap the elements at the equal and lesser indices
swap(eq, lt, A)
# Increase lesser index
lt += 1
# Increase equal index
eq += 1
elif A[eq] > pivot:
# Swap the elements at the equal and greater indices
swap(eq, gt, A)
# Decrease greater index
gt -= 1
else: # A[eq] == pivot
# Increase equal index
eq += 1

# Return lesser and greater indices
return lt, gt

elements = [11,9,29,7,2,15,28]
# elements = ["mona", "dhaval", "aamir", "tina", "chang"]
quicksort(elements, 0, len(elements)-1)
print(elements)

tests = [
[11,9,29,7,2,15,28],
[3, 7, 9, 11],
[25, 22, 21, 10],
[29, 15, 28],
[],
[6]
]

try:
# Your script's entry point, e.g., function calls
for elements in tests:
quicksort(elements, 0, len(elements)-1)
print(f'sorted array: {elements}')
except Exception as e:
print(f"Error occurred: {e}")
44 changes: 44 additions & 0 deletions 4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ### Exercise: Insertion Sort

# Compute the running median of a sequence of numbers. That is, given a stream of numbers, print out the median of the list so far on each new element.

# Recall that the median of an even-numbered list is the average of the two middle numbers in a *sorted list*.

# For example, given the sequence `[2, 1, 5, 7, 2, 0, 5]`, your algorithm should print out:

# ```
# 2
# 1.5
# 2
# 3.5
# 2
# 2
# 2
# ```

def find_median_value(elements):
if len(elements) == 1: #if the array has 1 element
return elements[0]
if len(elements) % 2 != 0: #if the array has an odd number of elements
return elements[(len(elements)//2)]
else: #if the array has an even number of elements
return ((elements[int(len(elements)/2)]+elements[int(len(elements)/2-1)])/2)

def insertion_sort(elements):
for i in range(1, len(elements)):
print(find_median_value(elements[0:i]))
anchor = elements[i]
j = i - 1
while j>=0 and anchor < elements[j]:
elements[j+1] = elements[j]
j = j - 1
elements[j+1] = anchor
print(find_median_value(elements))

# print (find_median_value([1,2,3,4,5,7,20,33,34]))
# print (find_median_value([1,2,3,4,8,7,20,33]))

elements = [2, 1, 5, 7, 2, 0, 5]
# print(elements[0:1])
insertion_sort(elements)
print(elements)
158 changes: 158 additions & 0 deletions 5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# ### Merge Sort Exercise

# Modify [merge_sort function](https://github.com/codebasics/data-structures-algorithms-python/blob/master/algorithms/5_MergeSort/merge_sort_final.py) such that it can sort following list of athletes as per the time taken by them in the marathon,
# ```
# elements = [
# { 'name': 'vedanth', 'age': 17, 'time_hours': 1},
# { 'name': 'rajab', 'age': 12, 'time_hours': 3},
# { 'name': 'vignesh', 'age': 21, 'time_hours': 2.5},
# { 'name': 'chinmay', 'age': 24, 'time_hours': 1.5},
# ]
# ```
# merge_sort function should take key from an athlete's marathon log and sort the list as per that key. For example,
# ```
# merge_sort(elements, key='time_hours', descending=True)
# ```
# This will sort elements by time_hours and your sorted list will look like,
# ```
# elements = [
# {'name': 'rajab', 'age': 12, 'time_hours': 3},
# {'name': 'vignesh', 'age': 21, 'time_hours': 2.5},
# {'name': 'chinmay', 'age': 24, 'time_hours': 1.5},
# {'name': 'vedanth', 'age': 17, 'time_hours': 1},
# ]
# ```
# But if you call it like this,
# ```
# merge_sort(elements, key='name')
# ```
# output will be,
# ```
# elements = [
# { 'name': 'chinmay', 'age': 24, 'time_hours': 1.5},
# { 'name': 'rajab', 'age': 12, 'time_hours': 3},
# { 'name': 'vedanth', 'age': 17, 'time_hours': 1},
# { 'name': 'vignesh', 'age': 21, 'time_hours': 2.5},
# ]
# ```

# [Solution](https://github.com/codebasics/data-structures-algorithms-python/blob/master/algorithms/5_MergeSort/merge_sort_exercise_solution.py)

# def merge_sort(arr):
# if len(arr) <= 1:
# return

# mid = len(arr)//2

# left = arr[:mid]
# right = arr[mid:]

# merge_sort(left)
# merge_sort(right)

# merge_two_sorted_lists(left, right, arr)

# def merge_two_sorted_lists(a,b,arr):
# len_a = len(a)
# len_b = len(b)

# i = j = k = 0

# while i < len_a and j < len_b:
# if a[i] <= b[j]:
# arr[k] = a[i]
# i+=1
# else:
# arr[k] = b[j]
# j+=1
# k+=1

# while i < len_a:
# arr[k] = a[i]
# i+=1
# k+=1

# while j < len_b:
# arr[k] = b[j]
# j+=1
# k+=1

# def merge_two_sorted_by_key(a,b,arr,key):
# len_a = len(a)
# len_b = len(b)

# i = j = k = 0

# while i < len_a and j < len_b:
# if a[i][key] <= b[j][key]:
# arr[k] = a[i]
# i+=1
# else:
# arr[k] = b[j]
# j+=1
# k+=1

# while i < len_a:
# arr[k] = a[i]
# i+=1
# k+=1

# while j < len_b:
# arr[k] = b[j]
# j+=1
# k+=1

def merge_sort_by_key(arr, key,descending=False):
if len(arr) <= 1:
return arr

mid = len(arr)//2

left = arr[:mid]
right = arr[mid:]

left = merge_sort_by_key(left,key,descending)
right = merge_sort_by_key(right,key,descending)

return merge_two_sorted_lists_by_key(left, right,key,descending)

def merge_two_sorted_lists_by_key(a,b,key,descending):
sorted_list = []

len_a = len(a)
len_b = len(b)

i = j = 0

while i < len_a and j < len_b:
if descending:
condition = a[i][key] > b[j][key] # Note the change here for descending
else:
condition = a[i][key] <= b[j][key]

if condition:
sorted_list.append(a[i])
i += 1
else:
sorted_list.append(b[j])
j += 1

while i < len_a:
sorted_list.append(a[i])
i+=1

while j < len_b:
sorted_list.append(b[j])
j+=1

return sorted_list


elements = [
{ 'name': 'vedanth', 'age': 17, 'time_hours': 1},
{ 'name': 'rajab', 'age': 12, 'time_hours': 3},
{ 'name': 'vignesh', 'age': 21, 'time_hours': 2.5},
{ 'name': 'chinmay', 'age': 24, 'time_hours': 1.5},
]

print(merge_sort_by_key(elements,key="age",descending=False))
109 changes: 109 additions & 0 deletions 6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# # Exercise: Shell Sort

# Sort the elements of a given list using shell sort, but with a slight modification. Remove all the repeating occurances of elements while sorting.

# Traditionally, when comparing two elements in shell sort, we swap if first element is bigger than second, and do nothing otherwise.

# In this modified shell sort with duplicate removal, we will swap if first element is bigger than second, and do nothing if element is smaller, but if values are same, we will delete one of the two elements we are comparing before starting the next pass for the reduced gap.



# For example, given the unsorted list `[2, 1, 5, 7, 2, 0, 5, 1, 2, 9, 5, 8, 3]`, after sorting using shell sort without duplicates, the sorted list would be:

# ```
# [0, 1, 2, 3, 5, 7, 8, 9]
# ```

# [23,3,1,56,34]

def shell_sort(arr):
size = len(arr)
gap = size//2

while gap > 0:
for i in range(gap,size):
anchor = arr[i]
j = i
while j>=gap and arr[j-gap]>anchor:
arr[j] = arr[j-gap]
j -= gap
arr[j] = anchor
gap = gap // 2

# [23,3,1,56,34]

# def shell_sort_remove_duplicates(arr):
# size = len(arr)
# gap = size//2

# while gap > 0:
# i = gap
# while i < size:
# anchor = arr[i]
# j = i
# while j>=gap:
# if arr[j-gap] > anchor:
# arr[j] = arr[j-gap]
# elif arr[j - gap] == anchor: # If elements are the same, prepare to delete one
# del arr[j] # Delete the current element
# size -= 1 # Decrease the size because we've removed an element
# i -= 1 # Adjust i since we've shifted the array elements
# break # Exit the inner while loop since we've handled the duplicate
# else:
# break
# j -= gap
# if j != i: # Ensures that we don't reset anchor if it was deleted
# arr[j] = anchor
# i += 1
# gap = gap // 2

def shell_sort_remove_duplicates(arr):
size = len(arr)
gap = size // 2

while gap > 0:
for i in range(gap, size):
anchor = arr[i]
j = i
while j >= gap and arr[j - gap] > anchor:
arr[j] = arr[j - gap]
j -= gap

# Place anchor at its correct position
arr[j] = anchor

# After each gap reduction, remove duplicates
i = 0
while i < (size - 1):
# If duplicate found
if arr[i] == arr[i+1]:
del arr[i+1]
size -= 1 # Reduce size after deletion
else:
i += 1 # Only increase i if no deletion happened to check next element

gap = gap // 2

return arr


tests = [
[89, 78, 61, 53, 23, 21, 17, 12, 9, 7, 6, 2, 1],
[],
[1,5,8,9],
[234,3,1,56,34,12,9,12,1300],
[5]
]

# for elements in tests:
# shell_sort(elements)
# print(elements)


elements2 = [2, 1, 5, 7, 2, 0, 5, 1, 2, 9, 5, 8, 3]
shell_sort_remove_duplicates(elements2)
print(elements2)

elements = [23,12,23,1,1,1,56,34,34]
shell_sort_remove_duplicates(elements)
print(elements)
155 changes: 155 additions & 0 deletions 7.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# # Exercise: Selection Sort

# Implement a Multi-Level Sort of a given list of dictionaries based on a given sorting order.
# If user wants to sort dictionary based on First Key 'A', Then Key 'B',
# they shall pass list of keys in the order of preference as a list ['A','B'].
# Your code should be able to sort list of dictionaries for any number of keys in sorting order list.

# Using this multi-level sort, you should be able to sort any list of dictionaries based on sorting order preference

# Example:
# A single dictionary entry contains two keys 'First Name' and 'Last Name'. the list should be sorted first based on 'First Name', then based on 'Last Name', w.r.t. common/same 'First Name' entries.

# for this, one shall past sorting order of preference list [ 'First Name' , 'Last Name' ]

# For this, Given the following sequence List:

# ```
# [
# {'First Name': 'Raj', 'Last Name': 'Nayyar'},
# {'First Name': 'Suraj', 'Last Name': 'Sharma'},
# {'First Name': 'Karan', 'Last Name': 'Kumar'},
# {'First Name': 'Jade', 'Last Name': 'Canary'},
# {'First Name': 'Raj', 'Last Name': 'Thakur'},
# {'First Name': 'Raj', 'Last Name': 'Sharma'},
# {'First Name': 'Kiran', 'Last Name': 'Kamla'},
# {'First Name': 'Armaan', 'Last Name': 'Kumar'},
# {'First Name': 'Jaya', 'Last Name': 'Sharma'},
# {'First Name': 'Ingrid', 'Last Name': 'Galore'},
# {'First Name': 'Jaya', 'Last Name': 'Seth'},
# {'First Name': 'Armaan', 'Last Name': 'Dadra'},
# {'First Name': 'Ingrid', 'Last Name': 'Maverick'},
# {'First Name': 'Aahana', 'Last Name': 'Arora'}
# ]
# ```


# Your algorithm should generate sorted list:

# ```
# [
# {'First Name': 'Aahana', 'Last Name': 'Arora'}
# {'First Name': 'Armaan', 'Last Name': 'Dadra'}
# {'First Name': 'Armaan', 'Last Name': 'Kumar'}
# {'First Name': 'Ingrid', 'Last Name': 'Galore'}
# {'First Name': 'Ingrid', 'Last Name': 'Maverick'}
# {'First Name': 'Jade', 'Last Name': 'Canary'}
# {'First Name': 'Jaya', 'Last Name': 'Seth'}
# {'First Name': 'Jaya', 'Last Name': 'Sharma'}
# {'First Name': 'Karan', 'Last Name': 'Kumar'}
# {'First Name': 'Kiran', 'Last Name': 'Kamla'}
# {'First Name': 'Raj', 'Last Name': 'Nayyar'}
# {'First Name': 'Raj', 'Last Name': 'Sharma'}
# {'First Name': 'Raj', 'Last Name': 'Thakur'}
# {'First Name': 'Suraj', 'Last Name': 'Sharma'}
# ]
# ```

def selection_sort(arr):
size = len(arr)
for i in range(size-1):
min_index = i
for j in range(min_index+1,size):
if arr[j] < arr[min_index]:
min_index = j
if i != min_index:
arr[i], arr[min_index] = arr[min_index], arr[i]

# def selection_sort_multi(arr,keys):
# size = len(arr)
# # for key in keys[-1::-1]:
# # for i in range(size):
# # min_index = i
# # for j in range(i+1,size):
# # if arr[j][key] < arr[min_index][key]:
# # min_index = j
# # arr[i], arr[min_index] = arr[min_index], arr[i]
# for key in reversed(keys):
# for i in range(size):
# min_or_max_index = i
# for j in range(i+1, size):
# if arr[j][key] < arr[min_or_max_index][key]:
# min_or_max_index = j
# # Swap the found minimum element with the first element
# arr[i], arr[min_or_max_index] = arr[min_or_max_index], arr[i]

# def selection_sort_multi(elements, sort_by_list):
# for sort_by in sort_by_list[-1::-1]:
# for x in range(len(elements)):
# min_index = x
# for y in range(x, len(elements)):
# if elements[y][sort_by] < elements[min_index][sort_by]:
# min_index = y
# if x != min_index:
# elements[x], elements[min_index] = elements[min_index], elements[x]

def selection_sort_multi(arr, keys):
size = len(arr)

# Adjusted loop to clearly iterate through keys in reversed order
for key in reversed(keys):
# Selection sort adapted for multiple keys
for i in range(size):
# Initially, min_index is the starting index
min_index = i
# Find the minimum element in remaining unsorted array
for j in range(i+1, size):
# Check condition for current sorting key
if arr[j][key] < arr[min_index][key]:
min_index = j

# Swap the found minimum element with the first element
arr[i], arr[min_index] = arr[min_index], arr[i]

def multilevel_selection_sort(elements, sort_by_list):
for sort_by in sort_by_list[-1::-1]:
for x in range(len(elements)):
min_index = x
for y in range(x, len(elements)):
if elements[y][sort_by] < elements[min_index][sort_by]:
min_index = y
if x != min_index:
elements[x], elements[min_index] = elements[min_index], elements[x]

tests = [
[89, 78, 61, 53, 23, 21, 17, 12, 9, 7, 6, 2, 1],
[],
[1,5,8,9],
[234,3,1,56,34,12,9,12,1300],
[78, 12, 15, 8, 61, 53, 23, 27],
[5]
]

# for elements in tests:
# selection_sort(elements)
# print(elements)

element2 = [
{'First Name': 'Raj', 'Last Name': 'Nayyar'},
{'First Name': 'Suraj', 'Last Name': 'Sharma'},
{'First Name': 'Karan', 'Last Name': 'Kumar'},
{'First Name': 'Jade', 'Last Name': 'Canary'},
{'First Name': 'Raj', 'Last Name': 'Thakur'},
{'First Name': 'Raj', 'Last Name': 'Sharma'},
{'First Name': 'Kiran', 'Last Name': 'Kamla'},
{'First Name': 'Armaan', 'Last Name': 'Kumar'},
{'First Name': 'Jaya', 'Last Name': 'Sharma'},
{'First Name': 'Ingrid', 'Last Name': 'Galore'},
{'First Name': 'Jaya', 'Last Name': 'Seth'},
{'First Name': 'Armaan', 'Last Name': 'Dadra'},
{'First Name': 'Ingrid', 'Last Name': 'Maverick'},
{'First Name': 'Aahana', 'Last Name': 'Arora'}
]

multilevel_selection_sort(element2,['First Name','Last Name'])
print(element2)
9 changes: 9 additions & 0 deletions util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import time
def time_it(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args,**kwargs)
end = time.time()
print(func.__name__ +" took " + str((end-start)*1000) + " mil sec")
return result
return wrapper