OCaml中如何将给定列表拆分为N个等长子列表?
实现问题定位
你当前编写的代码逻辑是按固定子列表长度n切分原列表,即每个子列表默认长度为n,末尾剩余不足n的元素单独成组,和需求「总共拆分为n个长度尽可能均等的子列表」的核心差异是缺少切分长度的动态计算逻辑。
核心实现逻辑
要实现固定块数的均等切分,只需要在递归切分过程中动态调整每一段的截取长度即可,计算逻辑如下:
- 记录两个递归状态:当前剩余待切分的列表、剩余需要生成的子列表数量
- 每一步计算当前剩余列表长度、剩余待生成块数,得出当前段的基础截取长度为
剩余列表长度 / 剩余块数 - 若
剩余列表长度 mod 剩余块数 > 0,则当前段多截取1个元素,保证所有子列表长度差不超过1,实现尽可能均等的效果
你之前实现的take和drop函数可以直接复用,不需要重写,仅需要调整split函数的递归逻辑即可。
修正后完整代码
let take l n = if n < 0 then raise (Invalid_argument "take") else let rec take_inner r l n = if n = 0 then List.rev r else match l with | [] -> raise (Invalid_argument "take") | h :: t -> take_inner (h :: r) t (n - 1) in take_inner [] l n let drop l n = let rec drop_inner n l = match l with | [] -> raise (Invalid_argument "drop") | _ :: t -> if n = 1 then t else drop_inner (n - 1) t in if n < 0 then raise (Invalid_argument "drop") else if n = 0 then l else drop_inner n l let split n l = if n <= 0 then raise (Invalid_argument "split") else let rec aux remaining_blocks remaining_l = match remaining_blocks with | 0 -> [] | _ -> let curr_len = List.length remaining_l in let curr_base = curr_len / remaining_blocks in let curr_rem = curr_len mod remaining_blocks in let take_cnt = curr_base + if curr_rem > 0 then 1 else 0 in let curr_block = take remaining_l take_cnt in let rest_l = drop remaining_l take_cnt in curr_block :: aux (remaining_blocks - 1) rest_l in aux n l
效果验证
针对你给出的测试用例:
- 输入列表为
[0;1;2;...;20](共21个元素),n=3 - 第一次递归:剩余3块、21个元素,截取长度7,取前7个元素
[0;1;2;3;4;5;6],剩余14个元素待分 - 第二次递归:剩余2块、14个元素,截取长度7,取接下来7个元素
[7;8;9;10;11;12;13],剩余7个元素待分 - 第三次递归:剩余1块、7个元素,截取长度7,取剩余所有元素
[14;15;16;17;18;19;20] - 最终输出和你给出的预期结果完全一致。
如果切分长度不能被n整除,比如22个元素切3份,会自动生成8、7、7长度的三个子列表,长度差最大为1,符合「尽可能相等」的要求。如果传入的n大于列表总长度,会自动在前几个子列表放单个元素,后续子列表为空列表,不会抛出异常。
内容的提问来源于stack exchange,提问作者VDS-Atomic
相关产品推荐
相关产品推荐

