def bucket_sort(array: Array, **kwargs) -> Array: """ Performs bucket sort on the given array. Parameters ========== array: Array The array which is to be sorted. start: int The starting index of the portion which is to be sorted. Optional, by default 0 end: int The ending index of the portion which is to be sorted. Optional, by default the index of the last position filled. Returns ======= output: Array The sorted array. Examples ======== >>> from pydatastructs import DynamicOneDimensionalArray as DODA, bucket_sort >>> arr = DODA(int, [5, 78, 1, 0]) >>> out = bucket_sort(arr) >>> str(out) "['0', '1', '5', '78']" >>> arr.delete(2) >>> out = bucket_sort(arr) >>> str(out) "['0', '1', '78']" References ========== .. [1] https://en.wikipedia.org/wiki/Bucket_sort Note ==== This function does not support custom comparators as is the case with other sorting functions in this file. The ouput array doesn't contain any `None` value. """ start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) #Find maximum value in the list and use length of the list to determine which value in the list goes into which bucket max_value = None for i in range(start, end + 1): if array[i] is not None: max_value = array[i] count = 0 for i in range(start, end + 1): if array[i] is not None: count += 1 if array[i] > max_value: max_value = array[i] number_of_null_values = end - start + 1 - count size = max_value // count # Create n empty buckets where n is equal to the length of the input list buckets_list = [[] for _ in range(count)] # Put list elements into different buckets based on the size for i in range(start, end + 1): if array[i] is not None: j = array[i] // size if j is not count: buckets_list[j].append(array[i]) else: buckets_list[count - 1].append(array[i]) # Sort elements within the buckets using Insertion Sort for z in range(count): _bucket_sort_helper(buckets_list[z]) # Concatenate buckets with sorted elements into a single array sorted_list = [] for x in range(count): sorted_list.extend(buckets_list[x]) for i in range(end, end - number_of_null_values, -1): array[i] = None for i in range(start, end - number_of_null_values + 1): array[i] = sorted_list[i - start] if _check_type(array, DynamicArray): array._modify(force=True) return array
def cocktail_shaker_sort(array: Array, **kwargs) -> Array: """ Performs cocktail sort on the given array. Parameters ========== array: Array The array which is to be sorted. start: int The starting index of the portion which is to be sorted. Optional, by default 0 end: int The ending index of the portion which is to be sorted. Optional, by default the index of the last position filled. comp: lambda/function The comparator which is to be used for sorting. If the function returns False then only swapping is performed. Optional, by default, less than or equal to is used for comparing two values. Returns ======= output: Array The sorted array. Examples ======== >>> from pydatastructs import OneDimensionalArray as ODA, cocktail_shaker_sort >>> arr = ODA(int, [5, 78, 1, 0]) >>> out = cocktail_shaker_sort(arr) >>> str(out) '[0, 1, 5, 78]' >>> arr = ODA(int, [21, 37, 5]) >>> out = cocktail_shaker_sort(arr) >>> str(out) '[5, 21, 37]' References ========== .. [1] https://en.wikipedia.org/wiki/Cocktail_shaker_sort """ def swap(i, j): array[i], array[j] = array[j], array[i] lower = kwargs.get('start', 0) upper = kwargs.get('end', len(array) - 1) comp = kwargs.get("comp", lambda u, v: u <= v) swapping = False while (not swapping and upper - lower >= 1): swapping = True for j in range(lower, upper): if _comp(array[j], array[j + 1], comp) is False: swap(j + 1, j) swapping = False upper = upper - 1 for j in range(upper, lower, -1): if _comp(array[j - 1], array[j], comp) is False: swap(j, j - 1) swapping = False lower = lower + 1 if _check_type(array, DynamicArray): array._modify(force=True) return array
def quick_sort(array: Array, **kwargs) -> Array: """ Performs quick sort on the given array. Parameters ========== array: Array The array which is to be sorted. start: int The starting index of the portion which is to be sorted. Optional, by default 0 end: int The ending index of the portion which is to be sorted. Optional, by default the index of the last position filled. comp: lambda/function The comparator which is to be used for sorting. If the function returns False then only swapping is performed. Optional, by default, less than or equal to is used for comparing two values. pick_pivot_element: lambda/function The function implementing the pivot picking logic for quick sort. Should accept, `low`, `high`, and `array` in this order, where `low` represents the left end of the current partition, `high` represents the right end, and `array` is the original input array to `quick_sort` function. Optional, by default, picks the element at `high` index of the current partition as pivot. Returns ======= output: Array The sorted array. Examples ======== >>> from pydatastructs import OneDimensionalArray as ODA, quick_sort >>> arr = ODA(int, [5, 78, 1, 0]) >>> out = quick_sort(arr) >>> str(out) '[0, 1, 5, 78]' >>> arr = ODA(int, [21, 37, 5]) >>> out = quick_sort(arr) >>> str(out) '[5, 21, 37]' References ========== .. [1] https://en.wikipedia.org/wiki/Quicksort """ from pydatastructs import Stack comp = kwargs.get("comp", lambda u, v: u <= v) pick_pivot_element = kwargs.get("pick_pivot_element", lambda low, high, array: array[high]) def partition(low, high, pick_pivot_element): i = (low - 1) x = pick_pivot_element(low, high, array) for j in range(low, high): if _comp(array[j], x, comp) is True: i = i + 1 array[i], array[j] = array[j], array[i] array[i + 1], array[high] = array[high], array[i + 1] return (i + 1) lower = kwargs.get('start', 0) upper = kwargs.get('end', len(array) - 1) stack = Stack() stack.push(lower) stack.push(upper) while stack.is_empty is False: high = stack.pop() low = stack.pop() p = partition(low, high, pick_pivot_element) if p - 1 > low: stack.push(low) stack.push(p - 1) if p + 1 < high: stack.push(p + 1) stack.push(high) if _check_type(array, DynamicArray): array._modify(force=True) return array
def timsort(array : Array,start,end)-> Array: """ The Timsort algorithm is considered a hybrid sorting algorithm because it employs a best-of-both-worlds combination of insertion sort and merge sort. The main characteristic of Timsort is that it takes advantage of already-sorted elements that exist in most real-world datasets.These are called natural runs. The algorithm then iterates over the list, collecting the elements into runs and merging them into a single sorted list. """ """ Parameters ======== array: Array The required array to be sorted start: int The starting index of the portion which is to be sorted. Optional, by default 0 end: int The ending index of the portion which is to be sorted. Optional, by default the index of the last position filled. Returns ======= output: Array The sorted list or array Examples ======== >>> from pydatastructs import OneDimensionalArray, timsort >>> arr = OneDimensionalArray(int,[-2, 7, 15, -14, 0, 15, 0] ) >>> timsort(arr, 0, 14) >>> [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6]] [ -14, -2, 0, 0, 7, 15, 15] >>> from pydatastructs import OneDimensionalArray, timsort >>> arr = OneDimensionalArray(int,[3, 2, 1]) >>> timsort(arr, 0, 2) >>> [arr[0], arr[1], arr[2]] [1, 2, 3] References ========== .. [1] https://en.wikipedia.org/wiki/Timsort """ min_run = 32 n = len(array) # Start by slicing and sorting small portions of the # input array. The size of these slices is defined by # your `min_run` size. for i in range(0, n, min_run): insertion_sort(array, i, min((i + min_run - 1), n - 1)) # Now you can start merging the sorted slices. # Start from `min_run`, doubling the size on # each iteration until you surpass the length of # the array. size = min_run while size < n: # Determine the arrays that will # be merged together for start in range(0, n, size * 2): # Compute the `midpoint` (where the first array ends # and the second starts) and the `endpoint` (where # the second array ends) midpoint = start + size - 1 end = min((start + size * 2 - 1), (n-1)) # Merge the two subarrays. # The `left` array should go from `start` to # `midpoint + 1`, while the `right` array should # go from `midpoint + 1` to `end + 1`. merged_array = _merge( left= array[start:midpoint + 1], right= array[midpoint + 1:end + 1] ) # Finally, put the merged array back into # your array array[start:start + len(merged_array)] = merged_array # Each iteration should double the size of your arrays size *= 2 if _check_type(array, DynamicArray): array._modify(force=True)