如何解决rev_append vs追加或@
如果我们有两个列表 l1
和 l2
并且我们想要连接它们,我们可以使用 @
或 append
,它们在 O(n1)
中,其中 {{ 1}} 是 n1
的长度。或者我们可以根据文档使用 l1
:
rev_append
那么 equivalent to List.rev l1 @ l2,but rev_append is tail-recursive and more efficient.
比 rev_append
更有效率还是比 @
更有效率?当我们不关心顺序时,用它代替 List.rev + @
和 @
是否更好?
解决方法
OCaml 列表是不可变的。第二个列表不需要更改,但必须复制第一个列表,以便副本可以指向第二个列表。因此,您将不得不以某种方式遍历第一个列表。您所做的任何事情都不会改变追加的大 O 时间复杂度。
由于只能在列表的开头添加新元素,所以如果希望结果保持第一个列表的顺序,则需要以相反的顺序遍历第一个列表。
最明显的方法是递归调用,直到到达第一个列表的末尾,然后在每次递归调用返回时添加前缀。然而,这不是尾递归的。即,它将消耗与第一个列表的长度成正比的堆栈空间。当第一个列表很长时,您可能会耗尽堆栈空间(又名堆栈溢出)。
这就是 @
的工作方式。它需要的时间和堆栈空间与第一个列表的长度成正比。
另一个想法是放弃维护第一个列表的顺序。如果以相反的顺序为第一个列表添加前缀,则可以轻松地使操作尾递归。这就是 List.rev_append
的目的。它需要恒定的堆栈空间。
如果你想保持原来的列表顺序,又要使用常量栈空间,你可以反转第一个列表(用List.rev
),然后用List.rev_append
。
Plain List.rev_append
比 @
快,因为它不必进行内部函数调用——它可以只是一个循环。它也明显比 List.rev
加 List.rev_append
快。
总而言之,如果您不关心最终顺序,那么 List.rev_append
比 @
快,是的。它也不会溢出堆栈。它不会快很多,因为时间复杂度基本相同。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。