Нет, это не сортировка слиянием. Это insertionSort, который по сути является тем же алгоритмом, что и bubbleSort, в зависимости от того, как вы на него смотрите. На каждом шаге список синглтона равен merge
d накопленному упорядоченному списку на данный момент, поэтому фактически вставляется элемент этого синглтона.
Как уже заметили другие комментаторы, чтобы получить mergeSort (и, в частности, его эффективность), необходимо неоднократно делить задачу на примерно равные части (а не на «один элемент» и «остальные») . «Официальное» решение дает довольно неуклюжий способ сделать это. мне очень нравится
foldr (\ x (ys, zs) -> (x : zs, ys)) ([], [])
как способ разделить список на две части, но не посередине, а на элементы в четных и нечетных позициях.
Если, как и я, вам нравится иметь структуру на видном месте, вы можете сделать упорядоченные списки Monoid
.
import Data.Monoid
import Data.Foldable
import Control.Newtype
newtype Merge x = Merge {merged :: [x]}
instance Newtype (Merge x) [x] where
pack = Merge
unpack = merged
instance Ord x => Monoid (Merge x) where
mempty = Merge []
mappend (Merge xs) (Merge ys) = Merge (merge xs ys) where
-- merge is as you defined it
И теперь у вас есть сортировка вставками только по
ala' Merge foldMap (:[]) :: [x] -> [x]
Один из способов получить структуру mergeSort по принципу «разделяй и властвуй» — сделать ее структурой данных: бинарными деревьями.
data Tree x = None | One x | Node (Tree x) (Tree x) deriving Foldable
Я не применял здесь инвариант балансировки, но мог бы. Дело в том, что та же операция, что и раньше, имеет другой тип
ala' Merge foldMap (:[]) :: Tree x -> [x]
который объединяет списки, собранные из древовидного расположения элементов. Чтобы получить указанные договоренности, подумайте "какие минусы для Tree
?" и убедитесь, что вы сохраняете равновесие, используя ту же извилистость, которую я использовал в приведенной выше операции «разделения».
twistin :: x -> Tree x -> Tree x -- a very cons-like type
twistin x None = One x
twistin x (One y) = Node (One x) (One y)
twistin x (Node l r) = Node (twistin x r) l
Теперь у вас есть mergeSort путем построения двоичного дерева, а затем его слияния.
mergeSort :: Ord x => [x] -> [x]
mergeSort = ala' Merge foldMap (:[]) . foldr twistin None
Конечно, введение промежуточной структуры данных имеет любопытное значение, но вы можете легко вырезать его и получить что-то вроде
mergeSort :: Ord x => [x] -> [x]
mergeSort [] = []
mergeSort [x] = [x]
mergeSort xs = merge (mergeSort ys) (mergeSort zs) where
(ys, zs) = foldr (\ x (ys, zs) -> (x : zs, ys)) ([], []) xs
где дерево стало рекурсивной структурой программы.
person
pigworker
schedule
11.03.2015
mergeSortOfficial
реализует MergeSort или нет, меня поражает то, что он пересчитывает длину списка (дважды!) при каждом вызове. Это очень неэффективно; длина должна быть вычислена один раз и навсегда в начале. - person jub0bs   schedule 11.03.2015merge list [x]
, что равносильно вставкеx
в упорядоченноеlist
. Текущий код выглядит O(n^2). - person chi   schedule 11.03.2015