提问者:小点点

Python:中位数为3的快速排序


我正在尝试更改此快速排序代码以使用采用“中位数为3”的枢轴。

def quickSort(L, ascending = True): 
    quicksorthelp(L, 0, len(L), ascending)


def quicksorthelp(L, low, high, ascending = True): 
    result = 0
    if low < high: 
        pivot_location, result = Partition(L, low, high, ascending)  
        result += quicksorthelp(L, low, pivot_location, ascending)  
        result += quicksorthelp(L, pivot_location + 1, high, ascending)
    return result


def Partition(L, low, high, ascending = True):
    print('Quicksort, Parameter L:')
    print(L)
    result = 0 
    pivot, pidx = median_of_three(L, low, high)
    L[low], L[pidx] = L[pidx], L[low]
    i = low + 1
    for j in range(low+1, high, 1):
        result += 1
        if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
            L[i], L[j] = L[j], L[i]  
            i += 1
    L[low], L[i-1] = L[i-1], L[low] 
    return i - 1, result

liste1 = list([3.14159, 1./127, 2.718, 1.618, -23., 3.14159])

quickSort(liste1, False)  # descending order
print('sorted:')
print(liste1)

但是我真的不知道该怎么做。中位数必须是列表的第一个、中间和最后一个元素的中位数。如果列表有偶数个元素,中间成为前半部分的最后一个元素。

这是我的中位数函数:

def median_of_three(L, low, high):
    mid = (low+high-1)//2
    a = L[low]
    b = L[mid]
    c = L[high-1]
    if a <= b <= c:
        return b, mid
    if c <= b <= a:
        return b, mid
    if a <= c <= b:
        return c, high-1
    if b <= c <= a:
        return c, high-1
    return a, low

共3个答案

匿名用户

让我们首先为三个数字实现中位数三,所以是一个独立的函数。我们可以通过对三个元素的列表进行排序来做到这一点,然后返回第二个元素,例如:

def median_of_three(a, b, c):
    return sorted([a, b, c])[1]

现在对于范围low… high(包含low,排除high),我们应该确定我们应该构造三个中位数的元素是什么:

  1. 第一个元素:L[low]
  2. 最后一个元素L[High-1],和
  3. 中间元素(如果有两个这样的元素,取第一个)L[(low High-1)//2]

所以现在我们只需要将分区函数修补到:

def Partition(L, low, high, ascending = True):
    print('Quicksort, Parameter L:')
    print(L)
    result = 0 
    pivot = median_of_three(L[low], L[(low+high-1)//2], L[high-1])
    i = low + 1  
    for j in range(low + 1, high, 1): 
        result += 1
        if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
            L[i], L[j] = L[j], L[i]  
            i += 1  
    L[low], L[i-1] = L[i-1], L[low] 
    return i - 1, result

编辑:确定三个元素的中位数。

三个元素的中位数是位于另外两个值中间的元素。所以在casea

所以我们需要确定元素的顺序,这样我们就可以确定中间的元素。比如:

def median_of_three(a, b, c):
    if a <= b and b <= c:
        return b
    if c <= b and b <= a:
        return b
    if a <= c and c <= b:
        return c
    if b <= c and c <= a:
        return c
    return a

所以现在我们已经定义了三个中位数和四个if情况。

EDIT2:这仍然存在一个问题。在执行透视后,您将元素L[i-1]与原始代码中的L[low]交换(透视的位置)。但这当然不再起作用:因为现在透视可以位于三个维度中的任何一个。因此,我们需要使median_of_three(…)更智能:它不仅应该返回透视元素,还应该返回该透视的位置:

def median_of_three(L, low, high):
    mid = (low+high-1)//2
    a = L[low]
    b = L[mid]
    c = L[high-1]
    if a <= b <= c:
        return b, mid
    if c <= b <= a:
        return b, mid
    if a <= c <= b:
        return c, high-1
    if b <= c <= a:
        return c, high-1
    return a, low

现在我们可以解决这个问题:

def Partition(L, low, high, ascending = True):
    print('Quicksort, Parameter L:')
    print(L)
    result = 0 
    pivot, pidx = median_of_three(L, low, high)
    i = low + (low == pidx)
    for j in range(low, high, 1):
        if j == pidx: continue
        result += 1
        if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
            L[i], L[j] = L[j], L[i]  
            i += 1 + (i+1 == pidx)
    L[pidx], L[i-1] = L[i-1], L[pidx] 
    return i - 1, result

编辑3:清理它。

虽然上面看起来可行,但它相当复杂:我们需要让ij“跳过”枢轴的位置。

如果我们首先将枢轴移动到子列表的前面(因此移动到low索引),可能会更简单:

def Partition(L, low, high, ascending = True):
    print('Quicksort, Parameter L:')
    print(L)
    result = 0 
    pivot, pidx = median_of_three(L, low, high)
    L[low], L[pidx] = L[pidx], L[low]
    i = low + 1
    for j in range(low+1, high, 1):
        result += 1
        if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
            L[i], L[j] = L[j], L[i]  
            i += 1
    L[low], L[i-1] = L[i-1], L[low] 
    return i - 1, result

匿名用户

在“中位数为三”的快速排序版本中,你不仅要找到中位数作为支点,还要将最大值和最小值放在它们的位置上,这样一些支点已经完成了。换句话说,你想在这三个地方对这三个项目进行排序。(有些变体不希望它们以通常的方式排序,但我将在这里为您提供一个更简单易懂的版本。)

您可能不想在函数中执行此操作,因为函数调用在Python中相当昂贵,并且这种特定功能并不广泛有用。所以您可以执行这样的代码。假设您要排序的三个值在索引ijk中,其中i

if L(i) > L(j):
    L(i), L(j) = L(j), L(i)
if L(i) > L(k):
    L(i), L(k) = L(k), L(i)
if L(j) > L(k):
    L(j), L(k) = L(k), L(j)

可以进行一些优化。例如,您可能希望在透视过程中使用中位数,因此您可以更改代码以将L(j)的最终值存储在一个简单的变量中,从而减少数组查找。请注意,一般来说,您不能在少于三次的比较中执行此操作-您不能将其减少到两次比较,尽管在某些特殊情况下您可以这样做。

匿名用户

一种可能的方法是从左右位置随机选择中位数。

def median_of_three(left, right):
    """
    Function to choose pivot point
    :param left: Left index of sub-list
    :param right: right-index of sub-list
    """

    # Pick 3 random numbers within the range of the list
    i1 = left + random.randint(0, right - left)
    i2 = left + random.randint(0, right - left)
    i3 = left + random.randint(0, right - left)

    # Return their median
    return max(min(i1, i2), min(max(i1, i2), i3))