整数列表划分为K个等和子列表的代码优化与扩展问题
问题解答
1. 代码复杂度优化方案
你的原代码核心问题是暴力枚举所有可能的组合再筛选,这会导致指数级时间复杂度(O(2^n)),处理大列表必然超时。优化核心是用回溯+剪枝替代暴力枚举,只在必要时递归搜索,同时避免重复计算和无效分支。
优化思路:
- 提前校验可行性:先计算列表总和,若无法被K(先按K=2处理)整除,直接返回空列表,省去后续无效计算。
- 排序剪枝:将原列表排序后,遇到与前一个相同且未被选中的元素时跳过,避免生成重复子集。
- 回溯搜索:递归寻找和为目标值(总和/K)的子集,找到后直接用剩余元素组成另一子集,无需两两比对所有组合。
- 去重处理:通过排序和剪逻辑,避免生成重复的划分结果。
优化后的K=2版本代码:
def subarrays_equal_sum_optimized(l, k=2): total_sum = sum(l) if len(l) < k or total_sum % k != 0: return [] target = total_sum // k l.sort() result = [] used = [False] * len(l) def backtrack(start, current_sum, path): if current_sum == target: # 收集未使用元素作为另一子集 remaining = [l[i] for i in range(len(l)) if not used[i]] sorted_path = sorted(path) sorted_remaining = sorted(remaining) candidate = [sorted_path, sorted_remaining] # 避免反向重复(如[A,B]和[B,A]视为同一结果) if candidate not in result and [sorted_remaining, sorted_path] not in result: result.append(candidate) return if current_sum > target: return for i in range(start, len(l)): if used[i]: continue # 剪枝:跳过重复元素,减少无效搜索 if i > start and l[i] == l[i-1] and not used[i-1]: continue used[i] = True backtrack(i + 1, current_sum + l[i], path + [l[i]]) used[i] = False backtrack(0, 0, []) return result
优化效果:
- 时间复杂度从O(2n)降至O(n*2n)(实际因剪枝远低于此上限),大列表处理速度大幅提升。
- 避免了生成所有组合的内存开销,仅在找到有效子集时处理剩余元素。
2. 扩展支持任意K值的方案
要支持任意K,核心是递归划分:每次找到一个和为目标值的子集后,对剩余元素递归划分成K-1个子集,直到K=1时,剩余元素即为最后一个合法子集。
核心思路:
- 递归终止条件:当K=1时,剩余元素的和必然等于目标值(总和为K*target),直接将剩余元素作为最后一个子集加入结果。
- 回溯过程:找到有效子集后标记已使用元素,对未使用元素递归调用划分函数(K减1)。
- 全程保持排序和剪枝,避免重复结果。
支持任意K的完整代码:
def partition_k_equal_sum(l, k): total_sum = sum(l) n = len(l) if n < k or total_sum % k != 0: return [] target = total_sum // k l.sort(reverse=True) # 降序排序,更快触发超目标剪枝 result = [] used = [False] * n def backtrack(count, start, current_sum, current_groups): if count == k: # 复制当前组并加入结果 result.append([group.copy() for group in current_groups]) return if current_sum == target: # 完成一个组,开始下一组搜索 backtrack(count + 1, 0, 0, current_groups + [[]]) return if current_sum > target: return for i in range(start, n): if used[i]: continue # 剪枝:跳过重复元素 if i > start and l[i] == l[i-1] and not used[i-1]: continue used[i] = True current_groups[-1].append(l[i]) backtrack(count, i + 1, current_sum + l[i], current_groups) current_groups[-1].pop() used[i] = False # 额外剪枝:首个未用元素无法组成有效组时,直接返回 if current_sum == 0: return # 初始化:开始第一个组的搜索 backtrack(0, 0, 0, [[]]) # 统一组内排序,保证结果格式一致 for idx in range(len(result)): result[idx] = [sorted(group) for group in result[idx]] # 去重:避免顺序不同的相同划分 unique_result = [] seen = set() for partition in result: sorted_partition = tuple(tuple(group) for group in sorted(partition, key=lambda x: (len(x), x))) if sorted_partition not in seen: seen.add(sorted_partition) unique_result.append([list(group) for group in sorted_partition]) return unique_result
代码说明:
- 降序排序:优先处理大元素,更快触发"超目标值"剪枝,减少无效递归。
- 递归计数:用
count记录已找到的有效组数量,达到K时将当前划分加入结果。 - 去重处理:将每个划分的组排序后转为元组,存入集合避免重复结果。
测试示例:
A = [4, 3, 5, 6, 4, 3, 1] print(partition_k_equal_sum(A, 2)) # 输出:[[[3, 4, 6], [1, 3, 4, 5]], [[4, 4, 5], [1, 3, 3, 6]]],与原代码结果一致
内容的提问来源于stack exchange,提问作者Bsh
相关产品推荐
相关产品推荐

