已排序列表拆分:满足最大长度m与容差k的最少子列表数优化
问题与解决方法
需求与问题
给定已排序整数列表x,需计算满足以下条件的最少子列表数量:
- 子列表长度 ≤ m
- 子列表中最小元素 + 2k ≥ 最大元素
无需生成子列表,仅统计数量。
当前实现的split函数计算出的子列表数量偏高,核心问题在于拆分逻辑过于保守,没有尽可能让每个子列表容纳最多的合法元素。
原代码
def split(x,k,m,n): time = 0 if n<=m: try: if x[-1]<=x[0]+2*k: time +=1 else: time += split(x[0:n-1],k,m,n-1) time += split(x[n-1:n],k,m,1) except: pass else: time += split(x[0:n-m],k,m,n-m) time += split(x[n-m:n],k,m,m) return time
原代码问题分析
- 当列表长度
n>m时,直接硬切最后m个元素,完全不考虑这部分元素是否满足数值条件,可能导致不必要的拆分。 - 当
n<=m时,若整体不满足数值条件就拆分为前n-1个和最后1个元素,这种拆分方式会大幅增加子列表数量,因为没有尝试找到更大的合法子列表。
最优实现(贪心算法)
由于输入列表是已排序的,贪心策略是最优选择:从左到右遍历,每个子列表尽可能延伸到最大的合法位置,确保每个子列表容纳最多元素,从而最小化总数量。
def min_subarrays(x, k, m): count = 0 n = len(x) i = 0 while i < n: count += 1 start_val = x[i] # 先根据长度限制确定最远可能位置 max_pos = min(i + m - 1, n - 1) # 再根据数值条件往左调整到最远合法位置 while max_pos > i and x[max_pos] > start_val + 2 * k: max_pos -= 1 # 跳到下一个子列表的起点 i = max_pos + 1 return count
算法说明
- 每次从当前位置
i开始,将x[i]作为子列表的最小元素。 - 先根据长度限制
m确定最远可能的位置max_pos(不超过i+m-1,也不超出列表范围)。 - 由于列表已排序,从
max_pos往左找第一个满足x[max_pos] ≤ start_val + 2k的位置,这个位置就是当前子列表的最远终点。 - 计数加1,然后从终点的下一个位置开始处理剩余元素,直到遍历完整个列表。
内容的提问来源于stack exchange,提问作者Abdulrehman Zia
相关产品推荐
相关产品推荐

