【发布时间】:2021-09-25 15:07:38
【问题描述】:
我以 3 个元素的中位数为基准实现了这个版本的快速排序:
def rearrange_array(array, begin_index, end_index):
# choose 3 random indexes in the array
index1 = randint(begin_index, int(begin_index + (end_index - begin_index)/3))
index2 = randint(int(begin_index + (end_index - begin_index)/3), int(begin_index + 2*(end_index - begin_index)/3))
index3 = randint(int(begin_index + 2*(end_index - begin_index)/3), end_index)
if array[index1] > array[index2]:
tmp = array[index1]
array[index1] = array[index2]
array[index2] = tmp
if array[index2] > array[index3]:
tmp = array[index2]
array[index2] = array[index3]
array[index3] = tmp
if array[index1] > array[index2]:
tmp = array[index1]
array[index1] = array[index2]
array[index2] = tmp
# swap index2 and begin_index
tmp = array[index2]
array[index2] = array[end_index]
array[end_index] = tmp
def partition(array, start_index, end_index): # partition = conquer
rearrange_array(array, start_index, end_index)
pivot = array[end_index] # pivot is always the right most index
i = start_index - 1
# if an element is samller than pivot swap it in front
for j in range(start_index, end_index):
if array[j] <= pivot:
i += 1
# swap
tmp = array[i]
array[i] = array[j]
array[j] = tmp
# swap the pivot with the element after i
# since i only goes forward once something smaller than the pivot is found, we know that
# everything in front of i+1 will be smaller than pivot
tmp = array[i+1]
array[i+1] = array[end_index]
array[end_index] = tmp
return i + 1
def quicksort_recursive(array, start_index, end_index):
if start_index < end_index:
pivot_index = partition(array, start_index, end_index)
quicksort_recursive(array, start_index, pivot_index - 1)
quicksort_recursive(array, pivot_index, end_index)
为了测试排序算法,我在一个包含 10 万个元素的数组上使用了它,耗时约 16 秒
def get_array(length, maximum):
array = []
for _ in range(length):
array.append(randint(0, maximum))
return array
sys.setrecursionlimit(sys.getrecursionlimit() * 100)
a = get_array(100000, 100)
t = time()
quicksort_recursive(a, 0, len(a) - 1)
print(time() - t)
然后我将它与这个版本的快速排序进行了比较,只用了大约 0.8-1 秒:
def merge(array, left, right):
# the program will only get here once we call mergesort on
# a one element array
i = j = k = 0
# put the smallest one in the array
while i < len(left) and j < len(right):
if left[i] < right[j]:
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
k += 1
# if one array still has stuff left:
while i < len(left):
array[k] = left[i]
i += 1
k += 1
while j < len(right):
array[k] = right[j]
j += 1
k += 1
def mergesort_recursive(array):
# 1. devide the array in 2
if len(array) > 1:
# divide
middle_index = int(len(array)/2)
left = array[:middle_index]
right = array[middle_index:]
# conquer
mergesort_recursive(left)
mergesort_recursive(right)
# merge
merge(array, left, right)
我对此有点困惑,快速排序不应该比合并排序更快吗?我已经在 100 个元素的数组上进行了尝试,它确实将元素按正确的顺序排列,所以我不确定发生了什么
谢谢!
【问题讨论】:
-
这听起来像是 profiling 的工作
-
在预期的情况下,两者都是 O(n lg n),这意味着您确实需要担心隐藏在渐近复杂性中的常量。 Median-of-3 将帮助您避免快速排序的最坏情况 O(n^2) 行为,但代价是增加隐藏常数。 (特别是因为您要在组合中添加另一个函数调用,
mergesort避免了这种情况。如果您内联rearrange_array会发生什么?) -
Median-0f-3 在许多情况下非常昂贵,尤其是在您没有较低阈值截止的情况下。我建议您使用更简单的方法来调解最坏的情况,例如选择中点作为支点。 (我认为这就是@rcgldr 在他们的回答中所做的)
-
它确实避免了 O(n^2) 的最坏情况,这是否意味着它可能是 python 的问题(例如函数调用相当昂贵)?
标签: python algorithm sorting quicksort mergesort