Haskell合并排序实现

时间:2017-12-01 17:57:31

标签: sorting haskell mergesort

我想解决这个问题: 定义递归函数msort :: Ord a => [a] - > [a]实施 - 合并排序,可以通过以下两个规则指定:  长度为1的列表已经排序;  其他列表可以通过对两半进行排序并合并来进行排序 结果列表。

但我很难理解合并排序代码究竟是如何工作的。

这是我的代码::

merge :: Ord a => [a] -> [a] -> [a]
merge [] [] = []
merge (x:xs) (y:ys) | y > x =  (x:xs) ++  (y:ys)
                    |otherwise =  (y:ys) ++  (x:xs)


msort :: Ord a => [a] -> [a]
--base case , if length less than or equal 1 , then list is already  sorted
msort [] = [] -- how to implement base case?
msort (x:xs)  = merge  (take (length(x:xs) `div` 2 ) (x:xs)) (drop (length(x:xs) `div` 2 ) (x:xs))

2 个答案:

答案 0 :(得分:3)

您的merge功能有两个问题。

i)模式并非详尽无遗:如果其中一个列表是空的,那么就没有匹配。你应该

merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) = something

ii)你的something实际上并不是合并排序,它正在查看头部并根据头部的比较连接两个列表。例如,如果给定[1,4,5][2,3,4],则1将绑定到x2绑定到y,然后y > x保持,然后它会返回[1,4,5,2,3,4],显然没有排序,尽管两个给定的列表已经排序。

想象一下合并如下。给出两个列表,你只看到头部,然后选择较小的头部。取代你移除的那个,下一个元素滚动,你继续做同样的事情。这是一个递归过程;在每个选秀之后,你做同样的事情。在代码中:

merge (x:xs) (y:ys) | x < y     = x : merge xs (y:ys)
                    | otherwise = y : merge (x:xs) ys

对于实际排序:: Ord a => [a] -> [a],您还必须使用已经排序的一半,这也需要递归:

msort :: Ord a => [a] -> [a]
msort []  = []
msort [x] = [x]
msort xs  = merge firstHalfSorted secondHalfSorted
     where firstHalfSorted  = msort . fst $ halves
           secondHalfSorted = msort . snd $ halves
           halves           = splitAt halfPoint xs
           halfPoint        = length xs `div` 2

答案 1 :(得分:1)

因为我无法抗拒挑战,所以这是一个高效,增量,稳定的自上而下的合并。相同的设计和一些小的调整应该为严格的语言产生一个有效的(但非增量的)mergesort;懒惰仅在merge函数中使用,因此如果需要,应该可以做一些杂耍以避免它。

{-# language BangPatterns #-}

import Control.Monad

-- Divide a list into the first n `quot` 2 elements and
-- the rest. The front of the list is reversed.
half :: [a] -> ([a], [a])
half = join (go []) where
  go front ts [] = (front, ts)
  go front ts [_] = (front, ts)
  go front (t:ts) (_:_:hs) = go (t:front) ts hs

-- Some care is required to make the sort stable.
merge :: Ord a => Bool -> [a] -> [a] -> [a]
merge _ [] ys = ys
merge _ xs [] = xs
merge up xxs@(x : xs) yys@(y : ys)
  | (if up then (<=) else (<)) x y = x : merge up xs yys
  | otherwise = y : merge up xxs ys

msort :: Ord a => [a] -> [a]
msort = go True where
  go _ [] = []
  go _ xs@[_] = xs
  go !up xs = merge up frontSorted rearSorted where
    frontSorted = go (not up) front
    rearSorted = go up rear
    (front, rear) = half xs

使用懒惰的版本更容易编写和理解,但如果不注意控制某些编译器优化,则可能容易受subtle space leak的影响:

-- Split a list in half, with both halves in order
half :: [a] -> ([a], [a])
half = join go
  where
    go t [] = ([], t)
    go t [_] = ([], t)
    go (t : ts) (_:_:hs) = (t : front, rear)
      where (front, rear) = go ts hs -- Interesting laziness

merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge xxs@(x:xs) yys@(y:ys)
  | x <= y    = x : merge xs yys
  | otherwise = y : merge xxs ys

msort :: Ord a => [a] -> [a]
msort []  = []
msort [x] = [x]
msort xs  = merge (msort front) (msort rear)
  where (front, rear) = half xs

为了完整起见,我应该提到自下而上 mergesort在Haskell中看起来更自然。