mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-12-18 01:00:15 +00:00
108 lines
2.7 KiB
Python
108 lines
2.7 KiB
Python
|
"""
|
||
|
A Python implementation of the Median of Medians algorithm
|
||
|
to select pivots for quick_select, which is efficient for
|
||
|
calculating the value that would appear in the index of a
|
||
|
list if it would be sorted, even if it is not already
|
||
|
sorted. Search in time complexity O(n) at any rank
|
||
|
deterministically
|
||
|
https://en.wikipedia.org/wiki/Median_of_medians
|
||
|
"""
|
||
|
|
||
|
|
||
|
def median_of_five(arr: list) -> int:
|
||
|
"""
|
||
|
Return the median of the input list
|
||
|
:param arr: Array to find median of
|
||
|
:return: median of arr
|
||
|
|
||
|
>>> median_of_five([2, 4, 5, 7, 899])
|
||
|
5
|
||
|
>>> median_of_five([5, 7, 899, 54, 32])
|
||
|
32
|
||
|
>>> median_of_five([5, 4, 3, 2])
|
||
|
4
|
||
|
>>> median_of_five([3, 5, 7, 10, 2])
|
||
|
5
|
||
|
"""
|
||
|
arr = sorted(arr)
|
||
|
return arr[len(arr) // 2]
|
||
|
|
||
|
|
||
|
def median_of_medians(arr: list) -> int:
|
||
|
"""
|
||
|
Return a pivot to partition data on by calculating
|
||
|
Median of medians of input data
|
||
|
:param arr: The data to be checked (a list)
|
||
|
:return: median of medians of input array
|
||
|
|
||
|
>>> median_of_medians([2, 4, 5, 7, 899, 54, 32])
|
||
|
54
|
||
|
>>> median_of_medians([5, 7, 899, 54, 32])
|
||
|
32
|
||
|
>>> median_of_medians([5, 4, 3, 2])
|
||
|
4
|
||
|
>>> median_of_medians([3, 5, 7, 10, 2, 12])
|
||
|
12
|
||
|
"""
|
||
|
|
||
|
if len(arr) <= 5:
|
||
|
return median_of_five(arr)
|
||
|
medians = []
|
||
|
i = 0
|
||
|
while i < len(arr):
|
||
|
if (i + 4) <= len(arr):
|
||
|
medians.append(median_of_five(arr[i:].copy()))
|
||
|
else:
|
||
|
medians.append(median_of_five(arr[i : i + 5].copy()))
|
||
|
i += 5
|
||
|
return median_of_medians(medians)
|
||
|
|
||
|
|
||
|
def quick_select(arr: list, target: int) -> int:
|
||
|
"""
|
||
|
Two way partition the data into smaller and greater lists,
|
||
|
in relationship to the pivot
|
||
|
:param arr: The data to be searched (a list)
|
||
|
:param target: The rank to be searched
|
||
|
:return: element at rank target
|
||
|
|
||
|
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 5)
|
||
|
32
|
||
|
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 1)
|
||
|
2
|
||
|
>>> quick_select([5, 4, 3, 2], 2)
|
||
|
3
|
||
|
>>> quick_select([3, 5, 7, 10, 2, 12], 3)
|
||
|
5
|
||
|
"""
|
||
|
|
||
|
# Invalid Input
|
||
|
if target > len(arr):
|
||
|
return -1
|
||
|
|
||
|
# x is the estimated pivot by median of medians algorithm
|
||
|
x = median_of_medians(arr)
|
||
|
left = []
|
||
|
right = []
|
||
|
check = False
|
||
|
for i in range(len(arr)):
|
||
|
if arr[i] < x:
|
||
|
left.append(arr[i])
|
||
|
elif arr[i] > x:
|
||
|
right.append(arr[i])
|
||
|
elif arr[i] == x and not check:
|
||
|
check = True
|
||
|
else:
|
||
|
right.append(arr[i])
|
||
|
rank_x = len(left) + 1
|
||
|
if rank_x == target:
|
||
|
answer = x
|
||
|
elif rank_x > target:
|
||
|
answer = quick_select(left, target)
|
||
|
elif rank_x < target:
|
||
|
answer = quick_select(right, target - rank_x)
|
||
|
return answer
|
||
|
|
||
|
|
||
|
print(median_of_five([5, 4, 3, 2]))
|