Exemple #1
0
def bucket_sort(numbers):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    Running time: O(n + k) for best case and average case and O(n^2) for the worst case
    Memory usage: O(n + k)"""
    # FIXME: Improve this to mutate input instead of creating new output list

    # Find range of given numbers (minimum and maximum values)
    max_val = max(numbers)
    # use length of the list to determine which value in the list goes into which bucket
    size = max_val / len(numbers)

    # Create list of buckets to store numbers in subranges of input range
    buckets_list = []
    for x in range(len(numbers)):
        buckets_list.append([])

    # Loop over given numbers and place each item in appropriate bucket based on size
    for i in range(len(numbers)):
        j = int(numbers[i] / size)
        if j != len(numbers):
            buckets_list[j].append(numbers[i])
    else:
        buckets_list[len(numbers) - 1].append(numbers[i])

    # Sort elements within the buckets using Insertion Sort
    for z in range(len(numbers)):
        insertion_sort(buckets_list[z])

    # Concatenate buckets with sorted elements into a single list
    # Loop over buckets and append each bucket's numbers into output list
    output_list = []
    for x in range(len(numbers)):
        output_list = output_list + buckets_list[x]
    return output_list
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    Running time: O(n+k) where k is the number of buckets. The time complexity is depended on both
    n and k because if k is large then our algorithm is also increased because we have to traverse the buckets
    Memory usage: O(n) because as the n increases the space increases linearly, we are not doing processes in place so space is increased with n"""

    # creating the buckets, in this case it's a list within a list
    buckets = []
    for _ in range(num_buckets):
        buckets.append([])

    # loop through all the numbers
    for num in numbers:
        # determine the bucket index; using a given formula
        b_index = bucket_index_formula(num, numbers)
        # add to bucket
        buckets[b_index].append(num)
        # note: ultimately we want to sort this list but we will do that later

    # initialize empty bucket list
    complete_sorted_list = []
    # loop through bucket list with lists
    for array in buckets:
        # extend the list of the bucket into the complete sorted list if it's not empty
        if array != []:
            # sort then extend to completed_sorted_list 
            insertion_sort(array)
            complete_sorted_list.extend(array)
    return complete_sorted_list
Exemple #3
0
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    Running time:
    Best: O(n*k) You only need to traverse the original array once,
    and the new array or bucket k times, where k is the number of buckets that isn't empty
    Worst: O(n^2), There would be the same number of full buckets ast ehre are items in the original array, or else the array balues are so unevenly distributed that the sorting and arranging of the sub-arrays in the buckets takes more time
    Memory usage:O(n) memory usage will be linear as the algorithm only needs to create one extra data structure. Python uses dynamic arrays which allow for more flexibility"""
    arr = []

    # Create empty buckets
    for i in range(num_buckets):
        arr.append([])

    # isnert each elements from numbers into each bucket
    for num in numbers:
        index = int(num / max(numbers) * (num_buckets + 1))
        bucket = arr[index]
        bucket.append(num)

    # Then sort each element using insertion sort
    for bucket in arr:
        insertion_sort(bucket)

    # get sorted elements
    k = 0
    for i in range(num_buckets):
        for value in arr[i]:
            numbers[k] = value
            k += 1

    # return sorted numbers array
    return numbers
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
       sorting each with an iterative sorting algorithm, and merging results
       into a list in sorted order.

       Running time: O(n^2) where n is the number of elements in the items
                     list. The runtime of this function asymptotically scales
                     with the time take by the Insertion Sort algorithm, which
                     is used as a helper twice. Although in the average case
                     Insertion Sort runs in quadratic time, it is important to
                     note that it can also run in only linear time; when a list
                     of only 1 element is passed in, that counts as sorted.

       Memory usage: O(p). The space complexity of this function is dependent
                     upon that of the merge() function, which scales with the
                     size of the input items list.

    """
    # Split items list into approximately equal halves
    left, right = split(items)  # O(p) time
    # Sort each half using any other sorting algorithm
    insertion_sort(left)  # O(p^2 / 2) time
    insertion_sort(right)  # O(p^2 / 2) time
    # Merge sorted halves into one list in sorted order
    return merge(left, right)  # O(n + m) = O(p) time + space
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.

    Running time: O(n/2)^2 = O(n^2)/4 Why and under what conditions? each sorting methods take
    quadratic time and items is divided into two parts before being sorted.
    Memory usage: 2*O(n) Why and under what conditions? we are making a copy for items1 and items2"""
    # print("items: ", items)
    if len(items) <= 1:
        return items

    mid = len(items)//2 #split array in to two parts
    items1 = items[:mid]
    items2 = items[mid:]
    # sort each half
    selection_sort(items1)

    insertion_sort(items2)
    # store returned mereged
    sorted_items = merge(items1, items2, items)
    # replace all indexes in items with items new arrangemnt
    items[:] = sorted_items

    return items
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    Running time: O((n/b)^2), at n>=b it is equivalant to an O(n) counting sort
    Memory usage: O(n) add'l space"""
    min_value = 0
    max_value = 0
    bucket_list = []

    for number in numbers:
        if number < min_value:
            min_value = number
        if number > max_value:
            max_value = number

    bstep = (max_value - min_value) // num_buckets
    for _ in range(num_buckets):
        bucket_list.append([])

    for number in numbers:
        put_index = number // bstep
        if put_index >= len(bucket_list):
            put_index = len(bucket_list) - 1
        bucket_list[put_index].append(number)

    for bucket in bucket_list:
        insertion_sort(bucket)

    numbers.clear()
    for bucket in bucket_list:
        numbers.extend(bucket)
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    TODO: Running time: ??? Why and under what conditions?
    TODO: Memory usage: ??? Why and under what conditions?"""
    # TODO: Find range of given numbers (minimum and maximum values)
    # TODO: Create list of buckets to store numbers in subranges of input range
    # TODO: Loop over given numbers and place each item in appropriate bucket
    # TODO: Sort each bucket using any sorting algorithm (recursive or another)
    # TODO: Loop over buckets and append each bucket's numbers into output list
    # FIXME: Improve this to mutate input instead of creating new output list
    max_value = max(numbers)
    d_buckets = int(math.sqrt(len(numbers)))

    if num_buckets is None:
        buckets = [[] for _ in range(d_buckets)]
    else:
        buckets = [[] for _ in range(num_buckets)]

    for num in numbers:
        index = int(num / max_value * (d_buckets - 1))
        bucket = buckets[index]
        bucket.append(num)

    for bucket in buckets:
        insertion_sort(bucket)

    write_index = 0
    for bucket in range(len(buckets)):
        for value in buckets[bucket]:
            numbers[write_index] = value
            write_index += 1
Exemple #8
0
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: Best case : same as insertion sort O(n)
                  Worst case: same as insertion sort O(n^2) 
    Memory usage: O(n): we make a new array to merge it back"""
    #Split items list into approximately equal halves
    # Sort each half using any other sorting algorithm
    # Merge sorted halves into one list in sorted order

    #the first half of the list
    items1 = items[:len(items) // 2]
    #second half of the list
    items2 = items[len(items) // 2:len(items)]
    #sort each lists individually
    items1 = insertion_sort(items1)
    items2 = insertion_sort(items2)
    #merge the two sorted lists back
    merged = merge(items1, items2)

    #copy everything from merged to items one by one
    # for i in range(len(merged)):
    #     items[i] = merged[i]
    items[:] = merged

    return items
Exemple #9
0
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time:   O(n^2) See insertion sort.
    Memory usage:   O(n) An auxiliar array is created for each merge. """
    left, right = items[:len(items) // 2], items[len(items) // 2:]
    insertion_sort(left)
    insertion_sort(right)
    items[:] = merge(left, right)
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
#     then sorting each bucket and concatenating all buckets in sorted order.
#        Running time: 
#                 Best: 0(n*k) 
#             Why and under what conditions?
#                 You only need to traverse the original array once, 
#                 and the new array or buckets k times, where k is the number of 
#                 buckets that are not empty.
                
#                 Worst: 0(n^2) 
#             Why and under what conditions?
#                 There would be the same number of full buckets as there are 
#                 items in the original array, or else the array values are so unevenly 
#                 distributed that the sorting and arranging of the sub-arrays in
#                 the buckets takes up a lot of time
#        Memory usage: 
#                 0(n) 
#             Why and under what conditions?
#                 Memory usage is minimal and linear, even under the worst 
#                 conditions, as the algorithm only needs 
#                 to create one extra data structure in the 
#                 form of the array of arrays, and in python
#                 these are all dynamic arrays which have flexible space.
#             """
    buckets = []

    # Create empty buckets
    for i in range(num_buckets):
        buckets.append([])

    # isnert each elements from numbers into each bucket
    for num in numbers:
        if type(num) is int:
            index = int(int(num / int(max(numbers))) * int((num_buckets - 1)))
            bucket = buckets[index]
            bucket.append(num)
        else:
            index = ((int(num) / int(max(numbers))) * ((num_buckets - 1)))
            bucket = buckets[index]
            bucket.append(num)

    # Then sort each element using insertion sort
    for bucket in buckets:
        insertion_sort(bucket)

    # get sorted elements
    k = 0
    for i in range(num_buckets):
        for value in buckets[i]:
            numbers[k] = value
            k += 1

    # return sorted numbers array
    return numbers
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: O(n^2) always, to sort and merge
    Memory usage: O(n) to create new list (merged)"""
    items1 = insertion_sort(items[:len(items) // 2])
    items2 = insertion_sort(items[len(items) // 2:])

    merged = merge(items1, items2)

    items[:] = merged

    return items
Exemple #12
0
def split_sort_merge(items: List[int]) -> List[int]:
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: avg O(n^2) because of insertion_sort 
    Memory usage: O(n) because we're creating an array of (n) elements to merge"""
    # Split items list into approximately equal halves
    mid_index = len(items)//2
    first_half, second_half = items[:mid_index], items[mid_index:]
    # Sort each half using any other sorting algorithm
    insertion_sort(first_half)
    insertion_sort(second_half)
    # Merge sorted halves into one list in sorted order
    got_back = merge(first_half, second_half)  # (n)
    items[:] = got_back
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    TODO: Running time: ??? Why and under what conditions?
    TODO: Memory usage: ??? Why and under what conditions?"""

    middle_index = len(items) // 2
    right_half = items[middle_index:]
    insertion_sort(right_half)
    left_half = items[:middle_index]
    insertion_sort(left_half)

    for index, item in enumerate(merge(right_half, left_half)):
        items[index] = item
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    TODO: Running time: O(n) when sorted, O(n^2) average and worst(reverse sort)
    TODO: Memory usage: O(n) add'l space"""
    center = len(items) // 2
    items1 = items[:center]
    items2 = items[center:]

    insertion_sort(items1)
    insertion_sort(items2)

    items.clear()
    items.extend(merge(items1, items2))
Exemple #15
0
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    TODO: Running time: ??? 2*O(num_buckets) + O(n) + insertion_sort. Because we 
    loop num_buckets times to initalize an array
    loop n times to place number in the right bucket loop num_buckets times
    TODO: Memory usage: ??? O(num_buckets)"""
    arr = []
    for i in range(num_buckets):
        arr.append([])

    # Put array elements in different buckets
    for j in numbers:
        index_b = int(num_buckets * j)
        arr[index_b].append(j)

    # Sort individual buckets
    for i in range(num_buckets):
        arr[i] = insertion_sort(arr[i])

    # concatenate the result
    k = 0
    for i in range(num_buckets):
        for j in range(len(arr[i])):
            numbers[k] = arr[i][j]
            k += 1
    return numbers
Exemple #16
0
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: O(n^2)
    Memory usage: O(n)"""
    if len(items) < 2:
        return items
    # Split items list into approximately equal halves
    split = len(items) // 2
    # Sort each half using any other sorting algorithm
    sorted1, sorted2 = insertion_sort(items[:split]), insertion_sort(items[split:])
    # Merge sorted halves into one list in sorted order
    for index, item in enumerate(merge(sorted1, sorted2)):
        items[index] = item
    return items
Exemple #17
0
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: O(nlogn) because best or worst case we are diviiding the array into two halves
    Memory usage: O(n) because we are only running through items[list]once and not creating more space within the function"""
    items_length = len(items)
    halfway = math.floor(items_length)/2
    # Split items list into approximately equal halves
    low = []
    high = []
    for  i in range(halfway):
        low.append(items[i])
        result_one = insertion_sort(low)
    for j in range(halfway+1, items_length-1):
        high.append(items[j])
        result_two = insertion_sort(high)
    return merge(result_one,result_two)
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: O(n^2) since insertion sort takes O(n^2) for each n/2 halves
    and then merging and updating takes O(n).
    Memory usage: O(n^2) merging function returns a new variable after merging"""
    mid = len(items) // 2 

    # Split items list into approximately equal halves
    left = items[:mid]
    right = items[mid:]

    #  Sort each half using any other sorting algorithm
    insertion_sort(left) 
    insertion_sort(right) 
    # Merge sorted halves into one list in sorted order
    merged = merge(left, right)
    items[:] = merged
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    TODO: Running time: ??? Why and under what conditions?
    TODO: Memory usage: ??? Why and under what conditions?"""
    # TODO: Split items list into approximately equal halves
    # TODO: Sort each half using any other sorting algorithm
    # TODO: Merge sorted halves into one list in sorted order

    middle = len(items) // 2

    array1 = items[:middle]
    array2 = items[middle:]

    insertion_sort(array1)
    insertion_sort(array2)

    return merge(array1, array2)
Exemple #20
0
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: O(n)
    Memory usage: O(n)"""
    # Split items list into approximately equal halves
    # Sort each half using any other sorting algorithm
    # Merge sorted halves into one list in sorted order

    items1, items2 = bisect_list(items)

    selection_sort(items1)

    insertion_sort(items2)

    items[:] = merge(items1,items2)

    return items
Exemple #21
0
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    Running time: O((n/2)^2) -> O(n^2)
        When the left and right halves are in reverse order
    Memory usage: O(n)
        New memory is created to store new ordered list"""
    # Split items list into approximately equal halves
    middle = len(items) // 2
    left = items[:middle]
    right = items[middle:]

    # Sort each half using any other sorting algorithm
    insertion_sort(left)  # O(n^2)
    bubble_sort(right)  # O(n^2)

    # Merge sorted halves into one list in sorted order
    # Overwrite content of original list with content of sorted list
    items[:] = merge(left, right)
Exemple #22
0
def bucket_sort(numbers, num_buckets=None):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    TODO: Running time: O(??)
    TODO: Memory usage: O(??)"""
    max_value = max(numbers)
    # dynamic bucket allocation
    d_buckets = int(math.sqrt(len(numbers)))

    # create our empty buckets (if they specify a
    # value we use that many buckets otherwise we do it dynamically)
    if num_buckets is None:
        buckets = [[] for _ in range(d_buckets)]
    else:
        buckets = [[] for _ in range(num_buckets)]

    # disperse our data into the buckets - O(n)
    for num in numbers:
        index = int(num / max_value * (d_buckets - 1))
        # get the index of the bucket that we are going to insert the item into
        # find the bucket based off of the index (after 'hash')
        bucket = buckets[index]
        # append the value to said bucket
        bucket.append(num)

    # sort the elements in each bucket using insertion sort - O(n)
    for bucket in buckets:
        insertion_sort(bucket)

    # keep track of the value to write to
    write_index = 0
    # merge all the buckets back together in sorted order - O(n)
    for bucket in range(len(buckets)):
        # loop through every value in the bucket
        for value in buckets[bucket]:
            # write the value (sorted) into our input array
            numbers[write_index] = value
            # increase the element of the place we are going to write to
            write_index += 1
def get_pivot(collection):
    """Returns the index of the pivot element in items, after applying the
       'median of three' algorithm.

       How a Pivot is Chosen: "Median of 3"
       1) Take three random indices in the collection (a Python list)/
       2) The pivot is the element that is the median of the above three
       3) The three elements go back in the array in sorted order, relative to
          whichever 3 index positions they were pulled from.
          (i.e. if the element pulled from the last index was found to the
           median, it would go back into items at the index the middle element
           orignally occupied).

    """
    # pull out three random indices in the collection - will revisit when
    # we can prevent the same index being chosen more than once
    # first_rand = randint(0, len(collection) - 1)
    # second_rand = randint(0, len(collection) - 1)
    # third_rand = randint(0, len(collection) - 1)
    first_index = 0
    mid_index = len(collection) // 2
    last_index = len(collection) - 1
    first, middle, last = (
        collection[first_index],
        collection[len(collection) // 2],
        collection[last_index]
    )
    sub_three = [first, middle, last]
    # sort the three
    insertion_sort(sub_three)
    # print(sub_three)
    # place back in the items array
    collection[first_index] = sub_three[0]
    pivot = collection[mid_index] = sub_three[1]
    collection[last_index] = sub_three[2]
    # print(pivot)
    # print(collection)
    # return the index position pivot
    return first_index
def split_sort_merge(items):
    """Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    TODO: Running time: ??? Why and under what conditions?
    TODO: Memory usage: ??? Why and under what conditions?"""
    # TODO: Split items list into approximately equal halves
    # TODO: Sort each half using any other sorting algorithm
    # TODO: Merge sorted halves into one list in sorted order
    if len(items) <= 4:
        return insertion_sort(items)
    else:
        return merge(split_sort_merge(items[:int(len(items) / 2)]),
                     split_sort_merge(items[int(len(items) / 2):]))
def bucket_sort(numbers=[2, 3, 1, 12, 4], num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    TODO: Running time: ??? Why and under what conditions?
    TODO: Memory usage: ??? Why and under what conditions?"""
    # TODO: Find range of given numbers (minimum and maximum values)
    max_val = max(numbers)
    min_val = min(numbers)

    size = max_val / len(numbers)
    
    # TODO: Create list of buckets to store numbers in subranges of input range
    buckets = [[] for _ in range(num_buckets)]

    # TODO: Loop over given numbers and place each item in appropriate bucket
    for num in numbers:
        index = num / size      # to find the index, divide the num by the size
        buckets[index].append(num)     # add the num to it's appropriate bucket
        
    # TODO: Sort each bucket using any sorting algorithm (recursive or another)
    # TODO: Loop over buckets and append each bucket's numbers into output list
    for i in range(len(buckets)):
        insertion_sort(buckets[i])
Exemple #26
0
 def test_insertion_sort_on_sorted_integers(self):
     # Positive test cases (examples) with lists of sorted integers
     assert insertion_sort([]) == []  # Empty lists are vacuously sorted
     assert insertion_sort([3]) == [3]  # Single item is trivially sorted
     assert insertion_sort([3, 3]) == [3, 3]  # Duplicate items are in order
     assert insertion_sort([3, 5]) == [3, 5]
     assert insertion_sort([3, 5, 7]) == [3, 5, 7]
     # new added test cases
     sample_1 = [3, 7, 9, 11, 13, 15, 16, 17, 19, 20]
     assert insertion_sort(sample_1) == sample_1
     sample_2 = [1, 2, 4, 5, 7, 8, 10, 15, 25, 25, 32,
                 37, 37, 38, 38, 39, 39, 42, 42, 47]
     assert insertion_sort(sample_2) == sample_2
     sample_3 = [6, 9, 13, 13, 16, 17, 21, 22, 22,
                 22, 23, 24, 25, 29, 30, 34, 34, 36, 40, 43]
     assert insertion_sort(sample_3) == sample_3
def bucket_sort(numbers, num_buckets=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    Running time: O(n + k) n being the values in the list and k being the 
    bucket's we iterate over.
    Memory usage: O(n + k) n being the values in the list and k being the 
    bucket's we iterate over. """

    num_len, max_num = len(numbers), max(numbers)

    # Create list of buckets to store numbers in subranges of input range
    buckets = [[] for _ in range(num_buckets)]

    # Used to put the elements in the buckets
    divider = (max_num + 1) / num_buckets

    # print(max_num/num_buckets)
    # print('Confused:', int(numbers[7] / (max_num/num_buckets)))
    # print('Witchcraft:', int(numbers[7] / ((max_num + 1)/num_buckets)))
    # print(int(numbers[7]/divider))

    # Loop over given numbers and place each item in appropriate bucket
    for i in range(num_len):
        j = int(numbers[i] / divider)
        buckets[j].append(numbers[i])

    # Sort each bucket using any sorting algorithm (recursive or another)
    for i in range(len(buckets)):
        insertion_sort(buckets[i])

    # Loop over buckets and mutate the input
    counter = 0
    for i in range(len(buckets)):
        for item in buckets[i]:
            numbers[counter] = item
            counter += 1
    print(numbers)
Exemple #28
0
def split_sort_merge(items):
    """
    Sort given items by splitting list into two approximately equal halves,
    sorting each with an iterative sorting algorithm, and merging results into
    a list in sorted order.
    where: N = length of the original list, n = length of the first half,
    m = length second half. Also n and m is approximately(almost) equal length.
    so n + m approximately 2n or 2m which is, 2n = N or 2m = N

    Running time: O(n^2) + O(m^2) = O(2n^2) using the properties above, overall
    O(N^2) running time
    Memory usage: O(N), because when we are merging two sorted list at the end
    we are creating new empty list with the same length of the original list.

    Args:
        items(list): unsorted list of elements

    Returns:
        merged_list(list): sorted list of elements of original list. Reference
        of the original list didnt change.
    """

    # splitting part
    mid = len(items) // 2
    # copy the first half of the array
    items_1 = items[:mid]  # O(n)
    # copy the second half of the array
    items_2 = items[mid:]  # O(m)

    # sorting the splitted lists using iterative algorithms
    bubble_sort(items_1)  # O(n^2) refer to the bubble sort docstring
    insertion_sort(items_2)  # O(m^2) refer to insertion sort docstring

    # now merge two sorted arrays
    merged_list = merge(items_1, items_2)  # O(n+m) = O(N)
    # to make sorting in place
    items[:] = merged_list
def bucket_sort(numbers, slots=10):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in count order."""
    buckets = []
    [buckets.append(list()) for x in range(slots)]
    for num in numbers:
        buckets[int(num * slots)].append(num)

    sorted = []
    for bucket in buckets:
        if len(bucket) > 0:
            bucket = insertion_sort(bucket)
            [sorted.append(bucket[x]) for x in range(len(bucket))]

    return sorted
def bucket_sort(numbers):
    """Sort given numbers by distributing into buckets representing subranges,
    then sorting each bucket and concatenating all buckets in sorted order.
    Running time: O(n + k) for best case + average case, O(n^2) for worst case
    Memory usage: O(n + k)"""
    # FIXME: Improve this to mutate input instead of creating new output list
    # Find range of given numbers (minimum and maximum values)
    max_val = max(numbers)

    # Use the length of the list to figure out which value in the list goes in what bucket
    # DIVIDER to check which bucket the number should be placed
    size = max_val / len(numbers)

    # Create list of buckets to store numbers in subranges of input range
    bucket_list = []
    for x in range(len(numbers)):
        bucket_list.append([])

    # Loop over given numbers and place each item in appropriate bucket
    for i in range(len(numbers)):
        # figure out which bucket the current number should be in
        j = int(numbers[i] / size)
        if j != len(numbers):
            bucket_list[j].append(numbers[i])
        else:
            bucket_list[len(numbers) - 1].append(numbers[i])

    # Sort each bucket using any sorting algorithm (recursive or another)
    for z in range(len(numbers)):
        insertion_sort(bucket_list[z])

    # Loop over buckets and append each bucket's numbers into output list
    outputList = []
    for x in range(len(numbers)):
        outputList = outputList + bucket_list[x]
    return outputList