【问题标题】:Implementing 3-way quicksort实现三向快速排序
【发布时间】:2016-05-01 22:28:55
【问题描述】:

我是算法的新手,我很困惑我作为作业编写的代码中的错误在哪里。我正在尝试在 Python 3 中实现一个快速排序算法,该算法处理数组中的相等值。

这是一个快速排序函数(a 代表数组):

def randomized_quick_sort(a, l, r):
    if l >= r:
        return
    k = random.randint(l, r)
    a[l], a[k] = a[k], a[l]
    m1, m2 = partition3(a, l, r)
    randomized_quick_sort(a, l, m1 - 1);
    randomized_quick_sort(a, m2 + 1, r);

这是我的分区函数:

def partition3(a, l, r):
    x, j, t = a[l], l, r
    for i in range(l + 1, r + 1):
        if a[i] < x:
            j +=1
            a[i], a[j] = a[j], a[i]
        elif a[i] > x:
            a[i], a[t] = a[t], a[i]
            t -=1
        else:
            j +=1
    a[l], a[j] = a[j], a[l]
    return j, t

【问题讨论】:

  • 我建议将标题中的“3-way”替换为“stable”,或者将其完全删除。从根本上说,所有种类都更少,相等,更大。一些排序算法在元素相等时保持元素顺序,称为“稳定”排序实现。 3-way 让我想到 3-way 区分或排序 3 个单独的列表,比将 3 个列表连接成一个列表并对其进行排序更有效。
  • @IceArdor:我很确定 Elen 指的是 3 路 partitioning(“Dutch national flag problem”)。来自维基百科:“特别是,quicksort 算法的变体必须对重复元素具有鲁棒性,需要一个三向分区函数,该函数将小于给定键(红色)、等于键(白色)和大于钥匙(蓝色)。”

标签: python quicksort


【解决方案1】:

你应该纠正你的分区函数:

这是一个工作示例:

def partition3(a, l, r):
   x, j, t = a[l], l, r
   i = j

   while i <= t :
      if a[i] < x:
         a[j], a[i] = a[i], a[j]
         j += 1

      elif a[i] > x:
         a[t], a[i] = a[i], a[t]
         t -= 1
         i -= 1 # remain in the same i in this case
      i += 1   
   return j, t

【讨论】:

  • 非常感谢!我把分区搞砸了,现在我看清楚了。
【解决方案2】:

这是一个在 python 中非常简单的快速排序实现。虽然它仍然是 nlogn,但可以进行许多性能优化。例如,可以在一次遍历而不是数组的 3 次遍历中将分区划分为小于、等于、大于。

def qsort(arr):
    if len(arr) <= 1: return arr
    pivot = arr[0]
    less    = [x for x in arr if x < pivot]
    equal   = [x for x in arr if x == pivot]
    greater = [x for x in arr if x > pivot]
    return qsort(less) + equal + qsort(greater)

要使该分区在数组的一次传递中发生,请创建如下辅助函数:

def partition(arr, pivot):
    less, equal, greater = [], [], []
    for val in arr:
        if val  < pivot: less.append(val)
        if val == pivot: equal.append(val)
        if val  > pivot: greater.append(val)
    return less, equal, greater

def qsort(arr):
    if len(arr) <= 1: return arr
    pivot = arr[0]
    less, equal, greater = partition(arr, pivot)
    return qsort(less) + equal + qsort(greater)

【讨论】:

  • 虽然您的代码是快速排序算法的可读实现,但它不是就地排序,它返回一个新列表而不是原始列表。
  • 你完全正确!并且在实现返回新列表的(更简单和更优雅的)案例之后,可以更轻松地完成改变原始列表的案例。但是,如果您具有函数式编程背景,那么变异列表很丑陋,只有在需要性能优化时才应该这样做。
  • 非常感谢@gnicholas 提供了一个非常令人担忧且美观的解决方案(当时这有点超出我的能力)!但是,我确实需要保持在为我提供的功能范围内,这就是我要求帮助我查找错误的原因。
  • @gnicholas 在这里想说的是,如果不先了解更简单的解决方案,您将无法理解更复杂的解决方案。一旦你了解了不可变的实现,就更容易找出原地(可变)实现的问题。
【解决方案3】:

for循环的另一种实现

def partition3(a, l, r):
    x = a[l]
    m1 = l
    m2 = l
    i = m1
    for i in range(l + 1, r + 1):
        if a[i] < x:
            a[i], a[m1] = a[m1], a[i]
            a[i], a[m2+1] = a[m2+1], a[i]
            m1 += 1
            m2 += 1
        elif a[i] == x:
            a[i], a[m2+1] = a[m2+1], a[i]
            m2 += 1
    return m1, m2

【讨论】:

    猜你喜欢
    • 2014-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2013-08-09
    • 2016-01-10
    • 1970-01-01
    相关资源
    最近更新 更多