You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

奶牛挤奶时间区间计算:全搜索算法从O(n²)向O(n)优化需求

我来帮你梳理下这个问题的优化思路和错误排查方向~

问题回顾

先明确你的核心需求:

  • 输入已按开始时间升序排列的挤奶时间段列表timesIS(还有按结束时间降序的timesDE
  • 计算两个关键值:至少1头奶牛挤奶的最长连续时长、无奶牛挤奶的最长间隔时长
  • 当前全搜索方案是O(n²)复杂度,大数据量下性能拉胯,要优化到接近O(n)
  • 自己写的优化版本在长列表测试中输出错误,预期结果是912 184,需要定位问题
优化全搜索到接近O(n)的可行方案

既然已经有了排序好的列表,完全可以用贪心+一次遍历的思路替代n²的全搜索,核心是利用排序特性避免重复查找:

1. 计算最长挤奶时长(区间合并法)

因为timesIS是按开始时间升序排列的,我们可以直接遍历合并重叠/相邻区间,再找出合并后最长的区间长度:

  • 初始化当前合并区间的startend为第一个时间段的起止时间
  • 从第二个时间段开始遍历:
    • 如果当前时间段的start ≤ 当前合并区间的end,说明两个时间段重叠或相邻,更新end为两者的最大值
    • 如果不重叠,先计算当前合并区间的长度并更新最长挤奶时长,再重置startend为当前时间段的起止
  • 遍历结束后,别忘了计算最后一个合并区间的长度,更新最长挤奶时长

这个过程是纯O(n)遍历,完全不需要nextCease函数的多次查找,效率直接拉满。

2. 计算最长无挤奶时长

基于合并后的区间列表,我们可以轻松算出最长空闲间隔:

  • 先拿到整个挤奶周期的起始(第一个合并区间的start)和结束(最后一个合并区间的end
  • 遍历合并后的区间,计算相邻两个区间的间隔(后一个区间的start - 前一个区间的end),记录最大的间隔值
  • 注:如果需要考虑挤奶周期外的空闲时间(比如从0到第一个挤奶开始的时间、最后一个挤奶结束到农场关门的时间),可以额外计算这些值并和区间间隔对比,但根据你的测试用例,应该只需要考虑挤奶周期内的间隔

这个步骤也是O(n)复杂度,整体算法的时间复杂度就接近O(n)了。

错误排查方向(针对长列表输出错误)

你的优化版本输出不符合预期,大概率是这些细节没处理好:

1. 区间合并的边界处理

  • 是否正确处理了相邻区间?比如前一个区间end=100,后一个区间start=100,这时候应该算连续挤奶,而不是产生空闲间隔
  • 遍历结束后,有没有忘记计算最后一个合并区间的长度?这会直接导致最长挤奶时长少算最后一段

2. 无挤奶时长的计算逻辑

  • 是否错误把挤奶周期外的时间算进去了?比如测试用例的预期结果可能只考虑挤奶时间段内的间隔,但你误把0到第一个挤奶开始的时间算成了空闲时长
  • 相邻区间间隔的计算是否搞反了顺序?比如写成了前一个区间start - 后一个区间end,结果会是负数,自然拿不到正确的最大值
  • 有没有漏算最后一个区间结束到挤奶周期结束的间隔?或者相反,多算了不需要的部分

3. 排序列表的一致性问题

你提到timesDE是按结束时间降序的同一列表,如果你的优化逻辑依赖这个列表,要检查排序是否正确:比如是否真的按结束时间降序,相同结束时间的元素排序是否符合预期,会不会因为排序错误导致查找逻辑出错(不过其实上面的O(n)方案根本不需要timesDE,如果你的优化版本还在依赖它,反而可能引入bug)

4. 数据类型或溢出问题

如果时间值很大(比如超过语言默认整数范围),有没有用合适的数据类型?比如某些语言中int溢出会导致计算结果完全错误

5. 测试用例的边界情况

  • 测试用例中是否存在所有区间都重叠的情况?或者所有区间都完全不重叠的情况?
  • 是否存在只有单个区间的情况?这时候最长挤奶时长就是该区间的长度,无挤奶时长应该是0(如果只考虑挤奶周期内的话)
参考伪代码

给你一个对应O(n)方案的伪代码,你可以和自己的优化版本对比找差异:

# 假设timesIS是按start升序排列的列表,每个元素为(start, end)
def calculate_milking_stats(timesIS):
    if not timesIS:
        return (0, 0)
    
    # 第一步:合并重叠/相邻区间
    merged_intervals = []
    curr_start, curr_end = timesIS[0]
    for s, e in timesIS[1:]:
        if s <= curr_end:
            # 重叠或相邻,合并区间
            curr_end = max(curr_end, e)
        else:
            merged_intervals.append((curr_start, curr_end))
            curr_start, curr_end = s, e
    # 别忘了添加最后一个合并的区间
    merged_intervals.append((curr_start, curr_end))
    
    # 第二步:计算最长挤奶时长
    max_milking = max(end - start for start, end in merged_intervals)
    
    # 第三步:计算最长无挤奶时长
    max_idle = 0
    for i in range(1, len(merged_intervals)):
        prev_end = merged_intervals[i-1][1]
        curr_start = merged_intervals[i][0]
        idle_duration = curr_start - prev_end
        if idle_duration > max_idle:
            max_idle = idle_duration
    
    # 如果需要考虑挤奶周期外的空闲时间,可取消下面两行注释
    # max_idle = max(max_idle, merged_intervals[0][0])
    # max_idle = max(max_idle, total_farm_time - merged_intervals[-1][1])
    
    return (max_milking, max_idle)

内容的提问来源于stack exchange,提问作者HelloWorld4444

火山引擎 最新活动