-
Notifications
You must be signed in to change notification settings - Fork 0
/
quickselect.py
93 lines (67 loc) · 2.28 KB
/
quickselect.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
#!/usr/bin/env python
# MEDIAN OF MEDIANS
# Reference: https://brilliant.org/wiki/median-finding-algorithm/
def median_of_medians(arr):
"""
Median of medians is an algorithm to select an approximate median as a pivot for a partitioning algorithm.
:param arr:
:return:
"""
if arr is None or len(arr) == 0:
return None
return select_pivot(arr, len(arr) // 2)
def select_pivot(arr, k):
"""
Select a pivot corresponding to the kth largest element in the array
:param arr: Array from which we need to find the median.
:param k: cardinality that represents the kth larget element in the array
:return: The value of the pivot
"""
# Divide array into chunks of 5
#chunks by taking i from 0 to 4, 5 to 9, 10 to 14, etc
chunks = [arr[i : i+5] for i in range(0, len(arr), 5)]
#sort each chunks
sorted_chunks = [sorted(chunk) for chunk in chunks]
#take the median of each chunk
medians = [chunk[len(chunk) // 2] for chunk in sorted_chunks]
#find the median of medians
if len(medians) <= 5:
pivot = sorted(medians)[len(medians) // 2]
else:
pivot = select_pivot(len(medians) // 2)
#partition the array around the pivot
p = partition(arr, pivot)
#is the pivot position at the k position?
if k == p:
#select that pivot
return pivot
if k < p:
#select a new pivot by looking on the left side of the partioning
return select_pivot(arr[0:p], k)
else:
#select a new pivot by looking on the right side of the partioning
return select_pivot(arr[p+1:len(arr)], k - p - 1)
def partition(arr, pivot):
"""
Partition the array around the given pivot
:param arr: array to be partitioned
:param pivot: pivot used for the partitioning
:return: final position of the pivot used as a partioning point
"""
left = 0
right = len(arr) - 1
i = 0
while i <= right:
if arr[i] == pivot:
i += 1
elif arr[i] < pivot:
arr[left], arr[i] = arr[i], arr[left]
left += 1
i += 1
else:
arr[right], arr[i] = arr[i], arr[right]
right -= 1
return left
arr = [1, 2, 3, 4, 5, 1000, 8, 9, 99]
pivot = median_of_medians(arr)
print(pivot)