我需要使用F#实现合并排序,为此,我需要2个辅助功能:拆分和合并,其中“拆分”将一个列表拆分为两个大小相差最大为1的较小列表。创建分割功能。这是到目前为止我实现的合并和拆分功能:
let rec merge (l: 'a list, m: 'a list)=
match l,m with
|[],[] -> []
|[],mi::mf -> mi::mf
|li::lf,[] -> li::lf
|li::lf, mi::mf -> if li<mi then li::merge (lf,mi::mf)
else mi::merge(li::lf,mf)
let split (l: 'a list)=
let n= l.Length
if n%2=1 then
for i in 1..n/2 do
let rec mergesort (l :'a list)=
let (left,right)=split l
if left.Length>1 || right.Length>1 then
merge(mergesort left,mergesort right)
else
merge(left,right)
我被困住了,我不知道如何使用模式匹配来遍历列表以完成拆分功能。考虑到拆分和合并正确,我也不确定mergesort是否正确。
此外,在我的部分匹配案例中,列表是[]
或ai::af
的邻居,所以我有点不确定,当我们写ai::af
来表示列表时,a1等于到列表是否仅包含一个元素的话?
答案 0 :(得分:1)
您也可以使用List.splitInto
来实现自己的拆分功能。
let rec mergeSort lst =
let rec merge = function
| l, [] -> l
| [], l -> l
| x::xs, y::ys -> if x < y
then x :: merge (xs, y::ys)
else y :: merge (x::xs, ys)
match List.splitInto 2 lst with
| [l1;l2] -> merge (mergeSort l1, mergeSort l2)
| l -> List.concat l
答案 1 :(得分:0)
这是一个简单的mergesort
实现的样子:
let split list =
let rec aux l acc1 acc2 =
match l with
| [] -> (acc1,acc2)
| [x] -> (x::acc1,acc2)
| x::y::tail ->
aux tail (x::acc1) (y::acc2)
in aux list [] []
let rec merge l1 l2 = // this version is NOT tail-recursive
match (l1,l2) with
| (x,[]) -> x
| ([],y) -> y
| (x::tx,y::ty) ->
if x <= y then x::merge tx l2
else y::merge l1 ty
let rec mergesort list =
match list with
| [] -> []
| [x] -> [x]
| _ -> let (l1,l2) = split list
in merge (mergesort l1) (mergesort l2)
精心设计的精确模式匹配是它的核心。
主要部分mergesort
递归应用长度大于1的列表的拆分,然后合并通过自我应用排序的拆分后的两半:
split
将参数list
减半成一个“一半”列表(l1,l2)
的元组,然后使用merge
来减半将自我应用的结果合并到l1
和l2
。 split
使用辅助函数aux
,该函数将任何列表转换为由相同元素组成的列表元组,这些元素的长度相差不超过1。
最后,merge
(上面以简单的方式实现,但显然不是尾部递归的方式)将列表对重新组合在一起,从而进行排序。它使用内部将参数组合到元组中来简化仅3种情况的参数模式匹配:non-empty,empty
,empty,non-empty
和non-empty,non-empty
。
使merge
尾递归并不是很困难,出于教学上的原因,我没有这么做。您可能会在this gist中找到尾递归版本。