这是一个采访问题:
找到可以通过给定数字的相同数字形成的下一个最大数字。
输入:4765,输出:5467
如果使用array
,那就不那么难了。
x
)y
)但是,在OCaml中,如果我们使用list
,那么我不知道该怎么做。似乎算法难以递归。
有什么建议吗?
编辑:
请注意我希望以实用的方式(使用列表)
修改
通过关注@gorestorm的建议,我实现了这样:
exception NotFound;;
let rev_list_of_int n =
let rec process n =
let x = n / 10 and y = n mod 10
in
if x = 0 && y = 0 then []
else
y::(process x)
in
process n;;
let find_next_by_list n =
let l = rev_list_of_int n
in
let rec find_x before_x x after_x =
match after_x with
| [] | _::[] -> (before_x, -1, after_x)
| hd1::hd2::tl ->
if hd1 > hd2 then (hd1::before_x, hd2, tl)
else find_x (hd1::before_x) x (hd2::tl)
in
let (bx, x, ax) = find_x [] (-1) l (* ax is rev *)
in
if x = -1 then raise NotFound
else
let rec find_y x path = function
| [] -> raise NotFound
| hd::tl ->
if hd > x then (path, hd, tl)
else find_y x (hd::path) tl
in
let (left, y, right) = find_y x [] (List.rev bx) (* left is rev, right is not *)
in
(* rev ax, then y, then right, then x, then rev left*)
(List.rev_append (y::ax) right) @ (x::List.rev left)
答案 0 :(得分:2)
为了以功能的方式实现这一点,以相反的顺序维护您的数字列表更有效 - 这样最快速变化的数字更接近列表的头部。这允许实现避免重新分配整个列表,从而提高分摊的性能。
给出digits
的反向列表(从最不重要到最重要):
Starting at the least-significant digit (the head of the reversed list):
look for the first digit smaller than its predecessor: call it "a_k"
save the list of digits after a_k as: "unchanged"
Starting again at the least-significant digit:
look for the first digit larger than a_k: call it "a_l"
Accumulate the output list functional-style, starting with "unchanged":
add a_l to the head of the list
starting a third time at the least-significant digit:
add each digit to the head of the output (reversing that portion of the list)
stopping before a_l
add a_k to the head of the output, instead of a_l
after skipping a_l, continue to add digits from original to output
stopping before a_k
简而言之:要实现功能风格,您必须在列表中构建“已修改”的交换/反向部分。您不必将列表保持在最低有效位数的第一个顺序(如上面的伪代码所假设的那样),但是如果这样做,您的next_permutation
函数将具有O(1)的摊销性能及时和分配的记忆。
[编辑:更正,只有当所有数字都不同时,O(1)表现才会......]
如果您确实希望以最重要的数字优先顺序维护数字,您仍需要进行类似的扫描并重建交换/反向区域,然后是未更改区域的有序副本
答案 1 :(得分:1)
只需按字典顺序生成下一个排列:
Find the largest index k such that a[k] < a[k + 1]. If no such index exists, the permutation is the last permutation.
Find the largest index l such that a[k] < a[l]. Since k + 1 is such an index, l is well defined and satisfies k < l.
Swap a[k] with a[l].
Reverse the sequence from a[k + 1] up to and including the final element a[n].
在wikipedia上找到。可以在here找到OCaml中的实现:
The following Ocaml code implements a variation on this algorithm (it returns the permutations of 1..n in reverse reverse-lexicographic order) :
(* exchange into l @ [x] @ accu x with the last element of l which is > x *)
let rec swap l x accu = match l with
| a::b::t when b > x -> a :: (swap (b::t) x accu)
| a::t -> (x::t) @ (a::accu)
| _ -> failwith "this can't happen"
;;
(* permut l m accu computes the permutation p' following p = rev(l)@m,
stores it into accu and recalls itself until p has no successor *)
let rec permut l m accu = match l,m with
| a::_, b::t when a > b -> let p = swap l b t in permut [] p (p::accu)
| _, b::t -> permut (b::l) t accu
| _, [] -> accu
;;
答案 2 :(得分:0)
正如Kwariz指出的那样(基本上),你可以使用与列表相同的解决方案。阵列解决方案可以在恒定时间内交换数字,但它仍然需要线性时间才能首先扫描它们。使用数字列表,您仍然可以进行线性扫描,还可以使用线性操作来交换数字。
有可能提出一个更漂亮的递归解决方案。问题是你正在使用列表中相当“全局”的属性。将它分解为通常的递归部分并不容易。但它并不太遥远。首先,如果您可以在列表的尾部执行操作,那么这也是整个列表的正确结果(重新添加头部)。