我想解决这个问题: 定义递归函数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))
答案 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
将绑定到x
,2
绑定到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中看起来更自然。