/ eg_sort.py
eg_sort.py
1 2 3 """ 4 Like a hand of cards: maintains sorted left sub-array and repeatedly inserts into it 5 O(n^2), in-place, stable 6 """ 7 def insertion_sort(l: list) -> None: 8 for i in range(1, len(l)): 9 # Repeatedly shift elements rightward 10 key = l[i] 11 j = i - 1 12 while j >= 0 and l[j] > key: 13 l[j + 1] = l[j] 14 j -= 1 15 # Place element i 16 l[j + 1] = key 17 18 19 """ 20 Maintains sorted left sub-array, and repeatedly selects the minimum element from the unsorted 21 sub-array to place at the end of the left sub-array 22 O(n^2), in-place, not stable 23 """ 24 def selection_sort(l: list) -> None: 25 for i in range(len(l) - 1): 26 # Find minimum element index in remaining sub-array 27 min_i = i 28 for j in range(i+1, len(l)): 29 if l[j] < l[min_i]: 30 min_i = j 31 # Swap min element with start of unsorted sub-array 32 l[i], l[min_i] = l[min_i], l[i] 33 34 """ 35 Merge two sorted lists in O(n) time. Requires O(n) extra space 36 """ 37 def merge(a: list, b: list) -> list: 38 a_i, b_i, c = 0, 0, [] 39 40 while a_i < len(a) and b_i < len(b): 41 if a[a_i] <= b[b_i]: # using '<=' here makes mergesort stable 42 c.append(a[a_i]) 43 a_i += 1 44 else: 45 c.append(b[b_i]) 46 b_i += 1 47 48 if a_i < len(a): 49 c.extend(a[a_i:]) 50 elif b_i < len(b): 51 c.extend(b[b_i:]) 52 53 return c 54 55 """ 56 Recursively sorts left, right sub-arrays and then merges with 57 linear merge sub-routine 58 O(n*log(n)), not in-place, stable 59 """ 60 def merge_sort(l: list) -> list: 61 if len(l) <= 1: 62 return l 63 mid = len(l) // 2 64 return merge(merge_sort(l[:mid]), merge_sort(l[mid:])) 65 66 67 """ 68 Rearrange arr[lo...hi] in-place in linear time such that all elements 69 less than pivot are left of it, and all elements greater than or equal to pivot 70 are right of it. 71 Return pivot index 72 lo and hi are inclusive indices within which to partition 73 """ 74 def partition(arr: list, lo: int, hi: int) -> int: 75 pivot = arr[hi] # arbitrarily choosing last element as pivot 76 pivot_idx = hi 77 hi -= 1 78 # maintain arr[left of lo] < pivot, arr[right of hi] >= pivot 79 while lo <= hi: 80 if arr[lo] < pivot: 81 lo += 1 82 else: 83 arr[lo], arr[hi] = arr[hi], arr[lo] 84 hi -= 1 85 86 arr[lo], arr[pivot_idx] = arr[pivot_idx], arr[lo] 87 return lo 88 89 90 """ 91 Partitions array, then recursively sorts left and right sub-arrays 92 Expected O(n*log(n)), in-place, not stable 93 """ 94 def quick_sort(arr: list, lo=None, hi=None) -> None: 95 if lo is None: 96 lo, hi = 0, len(arr) - 1 97 if hi <= lo: 98 return 99 pivot_idx = partition(arr, lo, hi) 100 quick_sort(arr, lo, pivot_idx - 1) 101 quick_sort(arr, pivot_idx + 1, hi) 102 103 104 105 def binary_search(arr: list, elt) -> int: 106 """ 107 :param arr: A sorted array 108 :param elt: The element to search for 109 :return: The index of the target element. -1 if not present 110 If target element is present multiple times, returns one of these indices 111 """ 112 lo, hi = 0, len(arr) - 1 113 while lo <= hi: 114 mid = (lo + hi) // 2 115 if arr[mid] == elt: 116 return mid 117 elif arr[mid] > elt: 118 hi = mid - 1 119 else: 120 lo = mid + 1 121 122 return -1 123 124 125 126 l = [4, 3, 2, 7, 8, 1, 5, 6] 127 # insertion_sort(l) 128 # selection_sort(l) 129 # print(merge_sort(l)) 130 131 quick_sort(l) 132 print(l) 133 134 for i in range(10): 135 print(binary_search(l, i))