我为union find
算法编写了一个OCaml程序。我写的这个算法不是最优的,是最简单的版本。
我把我的OCaml代码放在这里是因为我不确定这段代码是否足够好(尽管算法本身),尽管这段代码可以正常运行。
在我开始学习OCaml之后,这是我第一次写完一篇完整的工作内容,所以请通过复习来帮助我。
有用的建议可以帮助我提高我的OCaml技能。感谢
type union_find = {id_ary : int array; sz_ary : int array};;
let create_union n = {id_ary = Array.init n (fun i -> i);
sz_ary = Array.init n (fun i -> 1)};;
let union u p q =
let rec unionfy id_ary i =
let vp = id_ary.(p) in
let vq = id_ary.(q) in
if i < Array.length id_ary then begin
if i != q && id_ary.(i) = vp then id_ary.(i) <- vq;
unionfy id_ary (i + 1)
end
else print_string "end of union\n"
in
unionfy u.id_ary 0;;
let is_connected u p q = u.id_ary.(p) = u.id_ary.(q);;
首先,
我是否正确创建了union
(如union find
)的数据结构?
我应该在里面包含两个数组还是有更好的方法?
第二,
我在此代码中使用array
,但array
为mutable
,这对fp
没有好处吗?
有没有办法避免使用数组?
最后,
总的来说,这段代码是否足够好?
有什么可以改进吗?
P.S。我还没有使用OCaml的面向对象的位,因为我还没有学到那个部分。
答案 0 :(得分:4)
对代码的一些评论:
您似乎没有使用sz_ary。
您的代码会针对每个联合操作遍历整个数组。这对于标准(Tarjan)union-find来说是不正确的。对于线性数量的并集运算,您的代码会生成二次解。维基百科有标准算法:Disjoint-set Data Structure。
回答你的第二个问题:据我所知,union-find是一种算法,其中没有已知的功能(不可变)解决方案,其复杂性与最佳命令解决方案相同。由于数组只是从整数到值的映射,因此您始终可以使用映射将任何基于数组的解决方案转换为不可变的解决方案。据我所知,这将与渐近复杂度中最着名的解决方案相匹配;即,它会增加log n的额外因子。当然,还有一个常数因素可能足以成为一个问题。
我已经在OCaml中实现了union-find几次,我总是选择使用可变数据来完成它。但是,我还没有使用数组。我有一个基本对象的记录类型,我在每个记录中使用一个可变字段指向其父对象。要进行路径压缩,可以修改父指针以指向树的当前根。
答案 1 :(得分:2)
一些风格点:
不确定为什么unionfy
将id_ary作为参数,因为它在整个
不要将Array.init
与常量函数一起使用。只需使用Array.make
。
print_string "...\n"
相当于print_endline "..."
以下定义可以通过惩罚来清理
let union u p q =
至:let union {id_ary; _} p q
,以便对u
没有无关的引用。
let is_connected u p q = u.id_ary.(p) = u.id_ary.(q);;
这可能是个人选择,但我会摆脱:
let vp = id_ary.(p) in
let vq = id_ary.(q) in
或者至少将它们推到递归定义之上,以便明确它们是不变的。
编辑:更正版本
let union {id_ary;_} p q =
let (vp, vq) = (id_ary.(p), id_ary.(q)) in
let rec unionfy i =
if i < Array.length id_ary then begin
if i != q && id_ary.(i) = vp then id_ary.(i) <- vq;
unionfy (i + 1)
end else print_endline "end of union"
in unionfy 0;;