Improving naive qsort implementation

时间:2015-07-28 16:11:32

标签: haskell quicksort

I am starting to learn Haskell and I've been reading this page on Haskell's wiki, which reports this qsort implementation:

 qsort :: (Ord a) => [a] -> [a]
 qsort []     = []
 qsort (x:xs) = qsort less ++ [x] ++ qsort more
     where less = filter (<x)  xs
           more = filter (>=x) xs

followed by a warning that this is not the most efficient way to do it, and linking an article which shows an incredibly verbose version of the same algorithm. Just by looking at it, I though that that was not what I was learning Haskell for, and I wanted to make a better version of the initial qsort without sacrificing its elegance. I mostly concentrated on eliminating the need to run filter twice every call, and this is what I've come up:

filter' :: (a -> Bool) -> [a] -> ([a], [a])
filter' _ [] = ([], [])
filter' f a = filterInternal a ([], [])
  where
    filterInternal [] p = p
    filterInternal (x:xs) (l, t)
      | f x       = filterInternal xs (x:l, t)
      | otherwise = filterInternal xs (l, x:t)

qsort' :: (Ord a) => [a] -> [a]
qsort' [] = []
qsort' (x:xs) = qsort' less ++ [x] ++ qsort' more
  where
    (more, less) = filter' (>x) xs

But I am not sure this is really better. I mean, it works, but how does it compare to the initial version?

1 个答案:

答案 0 :(得分:0)

这是我在思考大致相同问题时提出的解决方案(请指出任何警告!)。我没有想太多关于空间复杂性(尽管不应该太糟糕),只是时间。真正杀死天真qsort的是O(n)操作(++)。因此,让我们使用一个新的数据结构(可折叠),以便我们快速连接。

{-# LANGUAGE DeriveFoldable #-}

import Data.Foldable
import Data.List

data Seq a = (Seq a) :++: (Seq a) | Leaf a | Empty deriving (Foldable)

然后,返回qsort'的修改后的Seq a将是

qsort' :: Ord a => [a] -> Seq a
qsort' []     = Empty
qsort' (x:xs) = qsort less :++: Leaf x :++: qsort more
    where (less, more) = partition (<x)  xs

qsort本身只是qsort = toList . qsort'

注意:涉及partition的修补程序可以更好地为您提供常数因素,但++ vs :++:表示qsort现在可以优于O(n^2) 。此外,大多数排序实现都比这更好。这一点只是为了尽可能地反映“天真”的实施。