-
Notifications
You must be signed in to change notification settings - Fork 8
/
quick sort.py
171 lines (136 loc) · 6.19 KB
/
quick sort.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
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 17 15:32:34 2018
@author: Administrator
"""
import random as rd
#okay. the epic one!
#quick sort, mmm, not so quick
#the idea of quick sort is be the quickest
#first thing first, we gotta pick a pivot number for the list
#normally, people use the first element as pivot number
#however, we may encounter a case that the first element is the largest or the smallest
#that would make our sorting a total failure
#here i use the median of 3 approach
#take the first, the last and the one in the middle
#and get the median of the three
#we use the median as a pivot number
#after that, we do the same trick as merge sort
#we have a left index and a right index
#we do traversal on both indices
#say the left index comes from the left part
#the right index comes from the right part
#we compare two elements with pivot number at the same time
#there are four cases assuming that we dont have any duplicate values in the list
#first case, left is larger than pivot, right is smaller than pivot
#so we swap both
#second case, left is larger than pivot, right is larger than pivot as well
#we freeze the left, and move right side index one step backwards
#right=right-1
#then if left is larger than pivot, right is smaller than pivot
#we back to the first case
#if it is still the second case, we repeat this procedure until we move to first case
#or left and right indices cross, we stop the sorting
#third case, left is smaller than pivot, right is smaller than pivot
#it is the opposite case of the second case
#fourth case, left is smaller than pivot, right is larger than pivot
#great, we do nothing but moving both indices closer to the centre
#these are four basic scenarios when we do both traversals
#when left and right indices cross, we stop the sorting
#and we insert the pivot number in where indices cross
#the next step is just like merge sort
#we divide the list in two halves (excluding the pivot number)
#we perform the same trick on both halves recursively
#until we reach the base case when there are only two elements in the list
#we sort those two elements with simple comparison
def quick_sort(target):
#the first step is to get a pivot number
#it only works on list with more than two elements
#otherwise there is no point of getting a pivot number
if len(target)>2:
#we take three elements, first, last and middle
#we get a new list
test=[target[0],target[len(target)//2],target[-1]]
#this is how we get the median
#there are only 3 elements
#so the sum of indices is 3
#0+1+2
#all we need to do is to exclude the maximum and the minimum indices
#we get the median s index
pivotindex=3-test.index(max(test))-test.index(min(test))
#this part is very confusing
#mostly due to simultaneous swap
#it is merely swapping
#if the median number index is 0
#we do nothing
#cuz we initialize pivot number at index 0 later
#if not
#we make a swap
#we use slicing method to get the index of the median in original list
#we use that index to get the actual element
#then we swap it with element 0
if pivotindex!=0:
target[target.index(test[pivotindex])] , target[0] = target[0] , target[target.index(test[pivotindex])]
#with the previous swap, we initialize pivot number at position 0
pivot=target[0]
#first index is at 1, cuz we wanna exclude pivot number
left=1
#last index is at the end of the list
right=len(target)-1
#here comes the real deal
#when left and right dont cross
#excluding a case when two indices equal to each other
while left-1<right:
#case 1
if target[left]>pivot and target[right]<pivot:
target[left],target[right]=target[right],target[left]
left-=1
#case 3
if target[left]<pivot and target[right]<pivot:
left+=1
#case 2
if target[left]>pivot and target[right]>pivot:
right-=1
#case 4
if target[left]<pivot and target[right]>pivot:
left+=1
right-=1
#when left and right indices cross
#we pop the pivot number and insert it at position left-1
#when indices cross, we are one step after the input list is sorted
#therefore, we insert pivot at left-1 instead of left
if left>=right:
target.insert(left-1,target.pop(0))
#the recursive part
#we do the same trick on two halves
target[:left-1]=quick_sort(target[:left-1])
target[left:]=quick_sort(target[left:])
#the base case
#when we are left with two elements in a sublist
#we just compare and return in reverse order
#u might ask, what about one element
#well, we dont have to do anything so no codes needed
if len(target)==2:
if target[0]>target[1]:
return target[::-1]
return target
#there is a constraint for quick sort
#duplicate values would jeopardize everything we have built
#to solve that issue, we must amend the criteria for selecting pivot number
#for simplicity, i remove duplicates from our test list
#to handle duplicate values which might affect pivot
#my idea is to insert an extra if function
#if elements not at position 0 equal to pivot
#we create a temporary list to collect them
#when we plan to insert pivot
#we insert the temporary list to the full list
#since we have recursive functions
#this approach to get a quick sort
#is kinda different from my textbook
#to see the alternative version
#plz click the link below
# http://interactivepython.org/runestone/static/pythonds/SortSearch/TheQuickSort.html
for i in range(100):
target=rd.sample([i for i in range(1000)],100)
if quick_sort(target)!=sorted(target):
print('Erreur')