import random

numbers = []
for i in range(10):
    numbers.append(random.randint(0,100))
print("Random List")
print(numbers)
Random List
[22, 20, 79, 98, 3, 73, 40, 69, 32, 7]

Warm Up

Discuss with a partner... What are some strategies you would use to sort this list? (Don't worry about writing code for now)

  • I would look for the smallest number in the list and write it in a new place on the left. I would then look for the new smallest number and place it to the right of that number and so on until all the numbers are written in the new list.

Explore

Get into groups of 3

We will be focusing on 4 algorithms today.

We will look at the first one together, Bubble Sort

What is happening with this sort?

  • These numbers are being sorted with bubble sort. It compares pairs of values and makes sure the smaller number is on the left. It repeats this process until it goes througha all the values. Then, it has to go through again to make sure the new values are also in order. This sorting algorithm is effective but slow.

In your groups you will each choose to be an expert on a sorting algorithm. Merge, Selection, and Insertion. Take about 5 minutes to read about your algorithm (Geek for Geeks linked below) and be ready to explain it to your other group members.

Merge

  • works by dividing an array into smaller subarrays, sorting each subarray, then merging the sorted subarrays back together.
  • repeated until entire array is sorted
  • time complexity of O(n log n)= can sort large arrays relatively quickly
  • stable sort = the order of elements with equal values is preserved during the sort Selection
  • works by repeatedly selecting the smallest (or largest) element from the unsorted portion of the list and moving it to the sorted portion of the list
  • The algorithm repeatedly selects the smallest (or largest) element from the unsorted portion of the list and swaps it with the first element of the unsorted portion.
  • After the N (size of the array) iteration, we will get a sorted array. Insertion
  • Insertion sort is efficient for small data values
  • works similar to the way you sort playing cards in your hands. The array is virtually split into a sorted and an unsorted part.
  • Values from the unsorted part are picked and placed at the correct position in the sorted part.
  • think left side of line is sorted and right side is not- keep moving line every time new number is sorted
  • Time Complexity: O(N^2)

Practice

[75, 17, 46, 80, 67, 45, 69, 79, 40, 0]

How would you sort this list with...

  • Bubble Sort
    • I would compare the first 2 values ("75" and "17") and make sure they are in order. In order to do this, I must switch 17 and 75. Then, I would compare 46 and 80 and leave it in that order. I would repeat this process until I have made it through all the pairs and they are in order (go through the list multiple times if needed).
  • Selection Sort
    • First, I would search for the smallest value in the list, which is 0. Then, I would place it at the very left end. I would repeat this step until the list is sorted.

[88, 39, 53, 39, 58, 43, 74, 81, 71, 51]

How would you sort this list with...

  • Merge Sort
    • I would split the list in half until the list cannot be slip anymore (1 value and 1 value) and then sort them. Once the halves are sorted. I would put them together. I would repeat this process until all the pieces are put together to form a fully sorted list.
  • Insertion Sort
    • I would start from the left, 88, and compare each value one by one. The numbers on the left become the sorted portion and the numbers on the right are not sorted. I would keep moving down the line until the entire list is sorted.

Sorting Words

Sorting strings works in the same way as integers. Using your expertise algorithm, sort the following list of random words.

import nltk
import random

nltk.download('words')  # Download the word list (only required once)

from nltk.corpus import words

english_words = words.words()
#print(len(english_words))  # Prints the number of words in the list

# You can now use the 'english_words' list in your code

words = []
for i in range(10):
    words.append(english_words[random.randint(0,len(english_words))])
print("Random List")
print(words)
Random List
['permalloy', 'unsifted', 'cupromanganese', 'glycosuric', 'isthmoid', 'blow', 'satirist', 'accentually', 'Hinduism', 'cephalomotor']
[nltk_data] Downloading package words to
[nltk_data]     /home/alexac54767/nltk_data...
[nltk_data]   Package words is already up-to-date!
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left_half = arr[:mid]
    right_half = arr[mid:]
    left_half = merge_sort(left_half)
    right_half = merge_sort(right_half)
    return merge(left_half, right_half)
def merge(left, right):
    merged = []
    left_index = 0
    right_index = 0
    while left_index < len(left) and right_index < len(right):
        if left[left_index].lower() < right[right_index].lower():
            merged.append(left[left_index])
            left_index += 1
        else:
            merged.append(right[right_index])
            right_index += 1
    while left_index < len(left):
        merged.append(left[left_index])
        left_index += 1
    while right_index < len(right):
        merged.append(right[right_index])
        right_index += 1
    return merged
words = ['alphabetarian', 'automanual', 'casabe', 'Cyclophorus', 'Caph', 'crile', 'phonautographically', 'rattleran', 'redshirt', 'conformal']
sorted_words = merge_sort(words)
print("unsorted:")
print(words)
print("sorted:")
print(sorted_words)
unsorted:
['alphabetarian', 'automanual', 'casabe', 'Cyclophorus', 'Caph', 'crile', 'phonautographically', 'rattleran', 'redshirt', 'conformal']
sorted:
['alphabetarian', 'automanual', 'Caph', 'casabe', 'conformal', 'crile', 'Cyclophorus', 'phonautographically', 'rattleran', 'redshirt']

Discuss

Answer the following with your group.

  • When should you use each algorithm? What makes an algorithm the right choice?
  • Given the following lists...
    • [0, 2, 6, 4, 8, 10]
      • insertion sort: is efficeint for small data sets
    • [Elephant, Banana, Cat, Dog, Apple]
      • selection sort: will only take n size of array to sort (and can easily find next letter of alphebet)
    • [29, 13, 83, 47, 32, 78, 100, 60, 65, 15, 24, 9, 40, 68, 53, 8, 90, 58, 39, 32, 34, 91, 74, 94, 49, 87, 34, 87, 23, 17, 27, 2, 38, 58, 84, 15, 9, 46, 74, 40, 44, 8, 55, 28, 81, 92, 81, 88, 53, 38, 19, 21, 9, 54, 21, 67, 3, 41, 3, 74, 13, 71, 70, 45, 5, 36, 80, 64, 97, 86, 73, 74, 94, 79, 49, 32, 20, 68, 64, 69, 1, 77, 31, 56, 100, 80, 48, 75, 85, 93, 67, 57, 26, 56, 43, 53, 59, 28, 67, 50]
      • Merge sort: can sort large data sets quickly Select the algorithm you believe is best for each, explain.

HACKS

Provided below is a Bubble Sort Algorithm sorting a list of dictionaries based off of selected key.

  • Now it's time to do some coding...

  • Run code and then research and answer these questions...

    • Is a list and/or dictionary in python considered a primitive or collection type? Why?
      • Lists and dictionaries are not primitave in python. They cannot store more than one type of data (ex:integer, string etc). - Is the list passed into bubble sort "pass-by-value" or "pass-by-reference? Describe why in relation to output.
      • The list passed into bubble sort is "pass-by-refrence" since bubble sort physically changes the original list.
  • Implement new cell(s) and/or organize cells to do the following.

    • Create your own list
    • Use your expertise sorting algorithm (selection, insertion, merge). Note, I got my bubble sort from Geek for Geeks and made modifications. Each student in a group should have a unique algorithm.
    • Test your list with my bubble sort
    • Test my list with your new sort
    • Research analysis on sorting: comparisons, swaps, time. Build this into your hacks.
    • Find a better way to print the data, key first, then other elements in viewable form.

Use the code below to help guide your adventure

"""
* Creator: Nighthawk Coding Society
Bubble Sort of a List with optimizations
"""

# bubble sorts a list of dictionaries, base off of provided key
def bubbleSort(list, key):
    n = len(list) - 1  # list are indexed 0 to n-1, len is n
    
    # Traverse through list with i index
    for i in range(n):
        swapped = False  # optimize code, so it exits if now swaps on inner loop

        # Inner traversal using j index
        for j in range(n-i):  # n-i as positions on right are in order in bubble
 
            # Swap if the element KeyN is greater KeyN1
            keyN = list[j].get(key)
            keyN1 = list[j+1].get(key)
            if keyN > keyN1:
                swapped = True
                list[j], list[j + 1] = list[j + 1], list[j]  # single line swap
         
        if not swapped:  # if no swaps on inner pass, list is sorted
            return  # exit function
    

if __name__ == "__main__":
    # list/dictionary sample
    list_of_people = [
    {"name": "Risa", "age": 18, "city": "New York"},
    {"name": "John", "age": 63, "city": "Eugene"},
    {"name": "Shekar", "age": 18, "city": "San Francisco"},
    {"name": "Ryan", "age": 21, "city": "Los Angeles"}
    ]
    
    # assuming uniform keys, pick 1st row as source of keys
    key_row = list_of_people[0]

    # print list as defined
    print("Original")
    print(list_of_people)
    
    for key in key_row:  # finds each key in the row
        print(key)
        bubbleSort(list_of_people, key)  # sort list of people
        print(list_of_people)
Original
[{'name': 'Risa', 'age': 18, 'city': 'New York'}, {'name': 'John', 'age': 63, 'city': 'Eugene'}, {'name': 'Shekar', 'age': 18, 'city': 'San Francisco'}, {'name': 'Ryan', 'age': 21, 'city': 'Los Angeles'}]
name
[{'name': 'John', 'age': 63, 'city': 'Eugene'}, {'name': 'Risa', 'age': 18, 'city': 'New York'}, {'name': 'Ryan', 'age': 21, 'city': 'Los Angeles'}, {'name': 'Shekar', 'age': 18, 'city': 'San Francisco'}]
age
[{'name': 'Risa', 'age': 18, 'city': 'New York'}, {'name': 'Shekar', 'age': 18, 'city': 'San Francisco'}, {'name': 'Ryan', 'age': 21, 'city': 'Los Angeles'}, {'name': 'John', 'age': 63, 'city': 'Eugene'}]
city
[{'name': 'John', 'age': 63, 'city': 'Eugene'}, {'name': 'Ryan', 'age': 21, 'city': 'Los Angeles'}, {'name': 'Risa', 'age': 18, 'city': 'New York'}, {'name': 'Shekar', 'age': 18, 'city': 'San Francisco'}]
# bubble sorts a list of dictionaries, base off of provided key
def bubbleSort(list, key):
    n = len(list) - 1  # list are indexed 0 to n-1, len is n
    
    # Traverse through list with i index
    for i in range(n):
        swapped = False  # optimize code, so it exits if now swaps on inner loop

        # Inner traversal using j index
        for j in range(n-i):  # n-i as positions on right are in order in bubble
 
            # Swap if the element KeyN is greater KeyN1
            keyN = list[j].get(key)
            keyN1 = list[j+1].get(key)
            if keyN > keyN1:
                swapped = True
                list[j], list[j + 1] = list[j + 1], list[j]  # single line swap
         
        if not swapped:  # if no swaps on inner pass, list is sorted
            return  # exit function
    

if __name__ == "__main__":
    # list/dictionary sample
    List_favs = [
    {"name": "Haseeb", "fav food": "bread"},
    {"name": "Ava", "fav food": "tacos"},
    {"name": "Alexa", "fav food": "mac and cheese"},
    ]
    
    # assuming uniform keys, pick 1st row as source of keys
    key_row = List_favs[0]

    # print list as defined
    print("Original")
    print(List_favs)
    
    for key in key_row:  # finds each key in the row
        print(key)
        bubbleSort(List_favs, key)  
        print(List_favs)
Original
[{'name': 'Haseeb', 'food': 'bread'}, {'name': 'Ava', 'food': 'tacos'}, {'name': 'Alexa', 'food': 'mac and cheese'}]
name
[{'name': 'Alexa', 'food': 'mac and cheese'}, {'name': 'Ava', 'food': 'tacos'}, {'name': 'Haseeb', 'food': 'bread'}]
food
[{'name': 'Haseeb', 'food': 'bread'}, {'name': 'Alexa', 'food': 'mac and cheese'}, {'name': 'Ava', 'food': 'tacos'}]
def merge_sort(arr):
    if len(arr) <= 1:
        return arr

    mid = len(arr) // 2
    left_half = arr[:mid]
    right_half = arr[mid:]

    left_half = merge_sort(left_half)
    right_half = merge_sort(right_half)

    return merge(left_half, right_half)


def merge(left, right):
    merged = []
    left_index = right_index = 0

    while left_index < len(left) and right_index < len(right):
        if left[left_index]["name"] < right[right_index]["name"]:
            merged.append(left[left_index])
            left_index += 1
        else:
            merged.append(right[right_index])
            right_index += 1

    merged.extend(left[left_index:])
    merged.extend(right[right_index:])

    return merged


List_favs = [
    {"name": "Alexa", "food": "mac and cheese"},
    {"name": "Ava", "food": "tacos"},
    {"name": "Haseeb", "food": "bread"},
]

sorted_favs = merge_sort(List_favs)

for item in sorted_favs:
    print("Name:", item["name"])
    print("Food:", item["food"])
    print()
Name: Alexa
Food: mac and cheese

Name: Ava
Food: tacos

Name: Haseeb
Food: bread