高效的冒泡排序

时间:2017-03-24 02:13:17

标签: haskell

显然,这只是我用来理解Haskell的一个例子。我不打算使用冒泡排序进行排序,我通常也不会在二次算法中通过列表优化单次迭代。

我希望在列表已经排序后停止冒泡排序。我写了这段代码,其中bubble返回,作为元组中的第二项,布尔状态告诉调用者是否进行了交换:

bubble (x:y:ys)
    | x > y = (y : fst (bubble (x:ys)), True)
    | otherwise = (x : fst (bubble (y:ys)), snd (bubble (y:ys)))
bubble x = (x, False)
bubblesort xs
    | snd (bubble xs) = bubblesort (fst (bubble xs))
    | otherwise = fst (bubble xs)

在我熟悉的语言中,它会非常低效,因为bubble (y:ys)bubble xs会被重新计算两次而没有明确的记忆。但鉴于Haskell函数没有副作用,我可以假设编译器会优化重复的调用,使我的代码有效吗?

如果没有,那么编写高效冒泡排序的好方法是什么?

我看过the approach检查列表是否已排序:

bubbleSort (x:y:xs) = if sorted thisSort then thisSort else bubbleSort thisSort
    where thisSort = (min x y) : bubbleSort ((max x y):xs)
bubbleSort x = x
sorted (x:y:xs) = if x <= y then sorted (y:xs) else False
sorted x = x

但是我试图避免通过列表进行一次额外的迭代(一旦bubbleSort没有执行交换,就需要再进行一次列表遍历以验证列表是否已排序。)

2 个答案:

答案 0 :(得分:6)

  

在我熟悉的语言中,它会非常低效,因为bubble (y:ys)bubble xs会被重新计算两次而没有明确的记忆。但鉴于Haskell函数没有副作用,我可以假设编译器会优化重复的调用,使我的代码有效吗?

不,你不能这么认为。这种称为公共子表达式消除的优化可能是一个相当棘手的权衡,因为您正在为空间交换时间。例如,如果重复的表达式会占用大量内存,那么计算它可能需要两次以便您可以立即使用它,而不是仅计算一次并将其留在内存中。出于这个原因,GHC在应用常见的子表达式消除时非常保守。请参阅this question有关与您的示例非常相似的示例,并this one进行深入讨论。

(在您的情况下,当然,不想两次计算bubble (y:ys)是完全合理的。您可以通过添加辅助定义来回避整个问题,如Lazersmoke's answer所示。)

答案 1 :(得分:3)

使用where子句:

bubble (x:y:ys)
  | x > y = (y : fst (bubble (x:ys)), True)
  | otherwise = (x : fst yys, snd yys)
  where
    yys = bubble (y:ys)
bubble x = (x, False)
bubblesort xs
  | snd bxs = bubblesort (fst bxs)
  | otherwise = fst bxs
  where
    bxs = bubble xs

GHC

>