-
Notifications
You must be signed in to change notification settings - Fork 410
/
sorting.py
217 lines (171 loc) · 7.78 KB
/
sorting.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
from time import sleep
import numpy as np
test = False
class Array:
full_array = None
def stack_it(self):
to_stack = np.array(self.values)
if not np.array_equal(to_stack, self.pile[-1]):
self.pile = np.vstack((self.pile, np.array(self.values)))
def set_all(self, values):
for i in range(len(self.values)):
self.values[i] = values[i]
self.stack_it()
for i in range(len(self.values)):
Array.full_array[self.lower_index + i] = values[i]
self.stack_it()
def __init__(self, values, lower_index=0):
self.lower_index = lower_index
self.values = list(values)
self.pile = np.array(self.values)
if Array.full_array == None:
Array.full_array = list(values)
def swap(self, index1, index2):
self.values[index2], self.values[index1] = self.values[index1], self.values[index2]
Array.full_array[self.lower_index + index2], Array.full_array[self.lower_index +
index1] = Array.full_array[self.lower_index + index1], Array.full_array[self.lower_index + index2]
self.stack_it()
def set(self, index, num):
self.values[index] = num
Array.full_array[self.lower_index + index] = num
self.stack_it()
def get_len(self):
return len(self.values)
def bubble_sort(nums): # n^2
# We set swapped to True so the loop looks runs at least once
swapped = True
while swapped:
swapped = False
for i in range(nums.get_len() - 1):
if nums.values[i] > nums.values[i + 1]:
# Swap the elements
nums.swap(i, i + 1)
# Set the flag to True so we'll loop again
swapped = True
def selection_sort(nums): # n^2
# This value of i corresponds to how many values were sorted
for i in range(nums.get_len()):
# We assume that the first item of the unsorted segment is the smallest
lowest_value_index = i
# This loop iterates over the unsorted items
for j in range(i + 1, nums.get_len()):
if nums.values[j] < nums.values[lowest_value_index]:
lowest_value_index = j
# Swap values of the lowest unsorted element with the first unsorted
# element
nums.swap(i, lowest_value_index)
def insertion_sort(nums): # n^2
# Start on the second element as we assume the first element is sorted
for i in range(1, nums.get_len()):
item_to_insert = nums.values[i]
# And keep a reference of the index of the previous element
j = i - 1
# Move all items of the sorted segment forward if they are larger than
# the item to insert
while j >= 0 and nums.values[j] > item_to_insert:
nums.set(j + 1, nums.values[j])
j -= 1
# Insert the item
nums.set(j + 1, item_to_insert)
def heap_sort(nums): # n * logn
def heapify(nums, heap_size, root_index):
# Assume the index of the largest element is the root index
largest = root_index
left_child = (2 * root_index) + 1
right_child = (2 * root_index) + 2
# If the left child of the root is a valid index, and the element is greater
# than the current largest element, then update the largest element
if left_child < heap_size and nums.values[left_child] > nums.values[largest]:
largest = left_child
# Do the same for the right child of the root
if right_child < heap_size and nums.values[right_child] > nums.values[largest]:
largest = right_child
# If the largest element is no longer the root element, swap them
if largest != root_index:
nums.swap(root_index, largest)
# Heapify the new root element to ensure it's the largest
heapify(nums, heap_size, largest)
n = nums.get_len()
# Create a Max Heap from the list
# The 2nd argument of range means we stop at the element before -1 i.e.
# the first element of the list.
# The 3rd argument of range means we iterate backwards, reducing the count
# of i by 1
for i in range(n, -1, -1):
heapify(nums, n, i)
# Move the root of the max heap to the end of
for i in range(n - 1, 0, -1):
nums.swap(i, 0)
heapify(nums, i, 0)
def merge_sort(nums, lower_index=0): # n * logn
def merge(left_list, right_list):
sorted_list = []
left_list_index = right_list_index = 0
# We use the list lengths often, so it's handy to make variables
left_list_length, right_list_length = len(left_list), len(right_list)
for _ in range(left_list_length + right_list_length):
if left_list_index < left_list_length and right_list_index < right_list_length:
# We check which value from the start of each list is smaller
# If the item at the beginning of the left list is smaller, add it
# to the sorted list
if left_list[left_list_index] <= right_list[right_list_index]:
sorted_list.append(left_list[left_list_index])
left_list_index += 1
# If the item at the beginning of the right list is smaller, add it
# to the sorted list
else:
sorted_list.append(right_list[right_list_index])
right_list_index += 1
# If we've reached the end of the of the left list, add the elements
# from the right list
elif left_list_index == left_list_length:
sorted_list.append(right_list[right_list_index])
right_list_index += 1
# If we've reached the end of the of the right list, add the elements
# from the left list
elif right_list_index == right_list_length:
sorted_list.append(left_list[left_list_index])
left_list_index += 1
return sorted_list
# If the list is a single element, return it
if nums.get_len() <= 1:
return nums.values
# Use floor division to get midpoint, indices must be integers
mid = nums.get_len() // 2
# Sort and merge each half
left_list = merge_sort(Array(nums.values[:mid], lower_index))
right_list = merge_sort(Array(nums.values[mid:], mid), mid)
nums.set_all(left_list + right_list)
# Merge the sorted lists into a new one
sorted_list = merge(left_list, right_list)
nums.set_all(sorted_list)
return sorted_list
def quick_sort(nums): # n^2
def partition(nums, low, high):
# We select the middle element to be the pivot. Some implementations select
# the first element or the last element. Sometimes the median value becomes
# the pivot, or a random one. There are many more strategies that can be
# chosen or created.
pivot = nums.values[(low + high) // 2]
i = low - 1
j = high + 1
while True:
i += 1
while nums.values[i] < pivot:
i += 1
j -= 1
while nums.values[j] > pivot:
j -= 1
if i >= j:
return j
# If an element at i (on the left of the pivot) is larger than the
# element at j (on right right of the pivot), then swap them
nums.swap(j, i)
# Create a helper function that will be called recursively
def _quick_sort(items, low, high):
if low < high:
# This is the index after the pivot, where our lists are split
split_index = partition(items, low, high)
_quick_sort(items, low, split_index)
_quick_sort(items, split_index + 1, high)
_quick_sort(nums, 0, nums.get_len() - 1)