-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquick.py
128 lines (111 loc) · 3.9 KB
/
quick.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
"""
Quicksort illustration.
There are many quicksort variations and tweaks; this file just covers some basics.
Note: in Python use sorted() instead (Timsort).
"""
import random
def quicksort_in_place(values):
"""
O(n log n) with constant additional memory. (Other than the recursion stacks!)
Reference: Introduction to Algorithms section 7.1
"""
def qs(start, end):
if start >= end:
return
# print values # Uncomment this to see the divide and conquer progression.
q = partition(start, end)
qs(start, q-1)
qs(q+1, end)
def partition(start, end):
# Illustration of regions in the partition:
#
# start i| |j |end
# |_________|__________|_____|pivot
# <= pivot | > pivot | |
#
pivot = values[end]
i = start - 1
for j in xrange(start, end):
# Order is determined by this comparison. <= for ascending smallest to largest, > for descending
if values[j] <= pivot:
i += 1
# Swap to move values into the region <= pivot.
values[i], values[j] = values[j], values[i]
# Finally, swap the pivot value[end] to the end of the <= pivot region at i+1.
values[i+1], values[end] = values[end], values[i+1]
return i+1
qs(0, len(values)-1)
return values
def quicksort_in_place_random(values):
"""
Use a random pivot.
O(n log n).
Reference: Introduction to Algorithms section 7.3
"""
def qs(start, end):
if start >= end:
return
# print values # Uncomment this to see the divide and conquer progression.
q = random_partition(start, end)
qs(start, q-1)
qs(q+1, end)
def partition(start, end):
pivot = values[end]
i = start - 1
for j in xrange(start, end): # end is exclusive, i.e. end will not be included in the range
if values[j] <= pivot:
i += 1
values[i], values[j] = values[j], values[i] # swap
values[i+1], values[end] = values[end], values[i+1]
return i+1
def random_partition(start, end):
assert start < end
i = random.randint(start, end) # randint is inclusive, end is included in the possible ints
values[end], values[i] = values[i], values[end] # Swpa the random pivot with the end
return partition(start, end)
qs(0, len(values)-1)
return values
def quicksort_copy(values):
"""
Perform a quick sort without modifying the original list.
Average case O(n log n). Memory usage O(n).
"""
if len(values) <= 1:
return values
#print values # Uncomment this to see the divide and conquer progression.
left = []
right = []
# Naive pivot selection.
pivot_ind = int(len(values) / 2)
pivot = values[pivot_ind]
pivot_list = []
for ind in range(0, len(values)):
x = values[ind]
if x < pivot:
left.append(x)
elif x > pivot:
right.append(x)
else:
assert x == pivot
pivot_list.append(x)
return quicksort_copy(left) + pivot_list + quicksort_copy(right)
def _test(func, input):
expected = sorted(input)
actual = func(input)
print '{} ===> {}'.format(input, list(actual))
if actual != expected:
raise Exception('FAIL Expected: {} Actual: {}'.format(expected, actual))
def _test_all(func):
print '____ Function: {}'.format(func)
_test(func, [1, 0])
_test(func, [0, 3, 2, 1, 4, 5, 7, 6, 8])
_test(func, [0, 3, 2, 1, 4, 5, 5, 5, 5, 5, 7, 6, 8])
_test(func, [0, 1])
_test(func, [0, 1, 2])
_test(func, [2, 1, 0])
_test(func, [2, 0, 1])
_test(func, [0, 3, 2, 1, 4, 5, 7, 6, 8]*3)
if __name__ == '__main__':
_test_all(quicksort_copy)
_test_all(quicksort_in_place)
_test_all(quicksort_in_place_random)