import random

numbers = []
for i in range(10):
    numbers.append(random.randint(0,100))
print("Random List")
print(numbers)
Random List
[88, 84, 97, 75, 49, 44, 16, 40, 96, 42]

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 most likely sort this list in order from greatest to least or least to greatest. I can look through the numbers in the list and place the smallest/greatest in the front.

Explore

Get into groups of 3

We will be focusing on 4 algorithms today.

We will look at the first one together, Bubble Sort

d

What is happening with this sort?

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

Selection

Insertion

Practice

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

How would you sort this list with...

  • Bubble Sort
  • Selection Sort

    Explain. Bubble Sort:I would sort this list with bubble sort by iterating through the list multiple times. Each time, I would go to the right and look at 2 consecutive elements - for example, 75 and 17. If the one on the right is smaller than the one on the left, I will switch the two, so here, I switch 75 and 17 since 17 is smaller than 75. I keep doing this until the list is sorted.Selection Sort: I will look at the biggest number and the smallest number in the list and then switch the 2 - 80 and 0. Then, I will look at the 2nd biggest and the 2nd smallest and switch - 79, 17. I keep doing this until the list is sorted.

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

How would you sort this list with...

  • Merge Sort
  • Insertion Sort

    Explain. Merge Sort:I will split the list until only individual elements remain - 88, 39, 53, 39, 58, 43, 74, 81, 71, 51. The elements are sorted and then merged into 1 sorted list.Insertion Sort: The process will be very similar to Bubble Sort except insertion will only iterate through the list once. It looks at 2 consecutive elements and switches them if the one on the right is smaller - 75 and 17. It also looks at the new consecutive elements after switching so that the whole list is sorted. For example, after switching 75 and 17, 75 is also bigger than 46 so the two are switched.

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
from nltk.corpus import words
nltk.download('words')  # Download the word list (only required once)
english_words = words.words()

def new_words():
    # You can now use the 'english_words' list in your code
    random_words = []
    for i in range(10):
        random_words.append(english_words[random.randint(0,len(english_words))])
    return random_words
        

print("Random List")
print(new_words())
Random List
['wickerwork', 'eyepiece', 'dissimilarly', 'viscerotropic', 'frush', 'slough', 'trawl', 'countercheck', 'amimia', 'hypho']
[nltk_data] Downloading package words to /home/taykim/nltk_data...
[nltk_data]   Package words is already up-to-date!
def insertion_sort(word_list):
    for i in range(1, len(word_list)):
        current_word = word_list[i]
        j = i - 1
        while j >= 0 and word_list[j] > current_word:
            word_list[j + 1] = word_list[j]
            j -= 1
        word_list[j + 1] = current_word

random_words = new_words()
print("Random List:")
print(random_words)

insertion_sort(random_words)
print("Sorted List:")
print(random_words)
Random List:
['seizable', 'supererogate', 'venerability', 'cloistress', 'corvillosum', 'chamaeprosopic', 'subassembly', 'bearwood', 'rakan', 'archgenethliac']
Sorted List:
['archgenethliac', 'bearwood', 'chamaeprosopic', 'cloistress', 'corvillosum', 'rakan', 'seizable', 'subassembly', 'supererogate', 'venerability']

Bubble Sort Popcorn Hack

Edits:

  • Len(list) - 1 changed to len(list)
  • Changing ranges to x-1 and x-i-1
  • Changing return to break
  • Changing the for loop so that if not swapped is outside
  • Putting words = new_words() at the end so that the function works better
def bubbleSort(word_list):
    x = len(word_list)

    for i in range(x - 1):
        swapped = False

        for j in range(x - i - 1):
            if word_list[j] > word_list[j + 1]:
                word_list[j], word_list[j + 1] = word_list[j + 1], word_list[j]
                swapped = True         
        if not swapped:
            break
        

words = new_words()
print(words)

bubbleSort(words)
print("Sorted List:")
print(words)
['gunnel', 'abjure', 'geopolitics', 'incommunicable', 'connexity', 'invigorative', 'abiology', 'prodigalish', 'claustral', 'rebop']
Sorted List:
['abiology', 'abjure', 'claustral', 'connexity', 'geopolitics', 'gunnel', 'incommunicable', 'invigorative', 'prodigalish', 'rebop']

Selection Sort Popcorn Hack

Edits:

  • Removing the use of keyV because it is unnecessary and using word_list[j] directly
  • Putting words = new_words() at the end so that the function works better
def selectionSort(word_list):
    n = len(word_list)
    
    for i in range(n):
        smallI = i
        smallV = word_list[i]

        for j in range(i + 1, n):
            if word_list[j] < smallV:
                smallI = j
                smallV = word_list[j]
        
        word_list[i], word_list[smallI] = word_list[smallI], word_list[i]
        

words = new_words()
print(words)

selectionSort(words)
print("Sorted List:")
print(words)
['wampee', 'casuistic', 'verdugoship', 'flatbottom', 'anaesthesia', 'coincidentally', 'balancement', 'ethnozoological', 'obstetrics', 'expressible']
Sorted List:
['anaesthesia', 'balancement', 'casuistic', 'coincidentally', 'ethnozoological', 'expressible', 'flatbottom', 'obstetrics', 'verdugoship', 'wampee']

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]
    • [Elephant, Banana, Cat, Dog, Apple]
    • [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] Select the algorithm you believe is best for each, explain.

1: Insertion Sort is the best option because there are only 2 elements that need to be switched and insertion only iterates through the list once, so it will be much faster than the other algorithms.

2: Selection Sort is the best option because the 2 elements that are misplaced are the "greatest" and "smallest" values in terms of alphabetical order. In this list, only Elephant and Apple need to be replaced so selection is much better.

3: Merge Sort is the best option because it is the only one with an efficient time complexity and works well for long lists.

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?
    • Is the list passed into bubble sort "pass-by-value" or "pass-by-reference? Describe why in relation to output.

Lists and dictionaries are collection types than primitive types. A collection type is a data type that can hold multiple values, whereas a primitive type represents a single value. Both lists and dictionaries contain multiple values. The list passed into the bubble sort is "pass-by-reference" because "pass-by-reference" means the original list is changed through the function. Since the bubble sort changes the order of the original list, the list is "pass-by-reference".

  • 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'}]

Merge Sort for lists

def mergeSort(lst):
    if len(lst) <= 1:
        return lst
    
    mid = len(lst) // 2
    left = lst[:mid]
    right = lst[mid:]

    left = mergeSort(left)
    right = mergeSort(right)

    return merge(left, right)

def merge(left, right):
    merged_list = []
    left_index = right_index = 0
    while left_index < len(left) and right_index < len(right):
        if left[left_index] < right[right_index]:
            merged_list.append(left[left_index])
            left_index += 1
        else:
            merged_list.append(right[right_index])
            right_index += 1
    merged_list.extend(left[left_index:])
    merged_list.extend(right[right_index:])
    return merged_list

num_list = [40, 20, 12, 50, 42, 17, 83, 29]
word_list = ["spoihpseoihg", "boieroiejef", "psaovh", "saophnn", "waohg"]

print("Original number list:")
print(num_list)
sorted_num_list = mergeSort(num_list)
print("Sorted number list:")
print(sorted_num_list)

print("Original word list:")
print(word_list)
sorted_word_list = mergeSort(word_list)
print("Sorted word list")
print(sorted_word_list)
            
Original number list:
[40, 20, 12, 50, 42, 17, 83, 29]
Sorted number list:
[12, 17, 20, 29, 40, 42, 50, 83]
Original word list:
['spoihpseoihg', 'boieroiejef', 'psaovh', 'saophnn', 'waohg']
Sorted word list
['boieroiejef', 'psaovh', 'saophnn', 'spoihpseoihg', 'waohg']

Merge Sort for dictionaries and testing with sample list

def mergeSortDictList(list_of_dicts, key):
    if len(list_of_dicts) <= 1:
        return list_of_dicts

    mid = len(list_of_dicts) // 2
    left = mergeSortDictList(list_of_dicts[:mid], key)
    right = mergeSortDictList(list_of_dicts[mid:], key)

    return mergeDictlist(left, right, key)


def mergeDictlist(left, right, key):
    merged_list = []
    left_index = right_index = 0

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

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

    return merged_list



print("Original List:")
for person in list_of_people:
    print(person)

sorted_name = mergeSortDictList(list_of_people, key="name")
sorted_age = mergeSortDictList(list_of_people, key="age")
sorted_city = mergeSortDictList(list_of_people, key="city")

print("Sorted by:")
print("Name:")
for person in sorted_name:
    print(person)
print("Age:")
for person in sorted_age:
    print(person)
print("City:")
for person in sorted_city:
    print(person)
Original List:
{'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'}
Sorted by:
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': 'Shekar', 'age': 18, 'city': 'San Francisco'}
{'name': 'Risa', 'age': 18, 'city': 'New York'}
{'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'}