闭区间合并算法

闭区间合并算法

题目:

给定 N 个闭区间 [begin; end],任意两个相邻或相交的闭区间可以合并为一个闭区间。例如,[1;2] 和 [2;3] 可以合并为 [1;3],[1;3] 和 [2;4] 可以合并为 [1;4],但是[1;2] 和 [3;4] 不可以合并。

目标:

合并这些可以合并的闭区间,求出最终的闭区间集合

代码实现:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * <p>
 * IntervalMerging<br>
 * 区间合并算法
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2021年04月20日 10:45
 */
@Slf4j
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@JsonIgnoreProperties(ignoreUnknown = true)
public class IntervalMerging<T> {
    /**
     * 区间 - 开始
     */
    T begin;
    /**
     * 区间 - 结束
     */
    T end;

    /**
     * 区间合并算法
     *
     * @param list       - 区间集合
     * @param comparator - 比较器
     * @param <T>        - 数据类型
     * @return List<IntervalMerging<T>>  - 区间合并
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2021/02/23 10:24
     */
    public static <T> List<IntervalMerging<T>> merger(List<IntervalMerging<T>> list, Comparator<T> comparator) {
        if (list.isEmpty() || list.size() <= 1) {
            // 单区间 直接返回
            return list;
        }
        // 区间集合排序
        list.sort((o1, o2) -> comparator.compare(o1.begin, o2.begin));
        // 区间合并 返回值
        List<IntervalMerging<T>> merger = new ArrayList<>();
        // 区间集合
        List<IntervalMerging<T>> copy = new ArrayList<>(list);
        // 当前区间
        IntervalMerging<T> now = list.get(0);
        copy.remove(now);
        list.forEach(
                item -> {
                    if (comparator.compare(now.begin, item.end) < 0 && comparator.compare(now.end, item.begin) > 0) {
                        // 交集 now 匹配合并 所有其他的 命中则合并移除
                        now.begin = comparator.compare(item.begin, now.begin) < 0 ? item.begin : now.begin;
                        now.end = comparator.compare(item.end, now.end) > 0 ? item.end : now.end;
                        copy.remove(item);
                        log.info("merge {} + {} -> {} ", now, item, now);
                    }
                }
        );
        merger.add(now);
        // copy 中剩下的都是跟now无交集的
        merger.addAll(merger(copy, comparator));
        return merger;
    }

}

测试代码:

public static void main(String[] args) {
        // 当前人员 的所有 任务 起止时间 记录
        List<IntervalMerging<LocalDateTime>> taskTimeArrayList = new ArrayList<>();
        // 当前人员 的所有 任务 起止里程 记录
        List<IntervalMerging<BigDecimal>> taskMileageArrayList = new ArrayList<>();
        // 遍历考勤数据,构建考勤起止时间、起止里程集合

        LocalDateTime s = LocalDateTimeUtil.parse("2007-12-03T00:15:30");
        LocalDateTime e = LocalDateTimeUtil.parse("2007-12-03T02:15:30");

        BigDecimal s1 = new BigDecimal("0.001");
        BigDecimal e1 = new BigDecimal("2.001");

        for (int i = 0; i < 10; i++) {
            if ((i & 1) == 1) {
                s = s.plusHours(1);
                e = e.plusHours(1);
                s1 = s1.add(new BigDecimal("1.00"));
                e1 = e1.add(new BigDecimal("1.00"));
            } else {
                s = s.plusHours(2);
                e = e.plusHours(2);
                s1 = s1.add(new BigDecimal("2.00"));
                e1 = e1.add(new BigDecimal("2.00"));
            }
            // 任务 起止时间 记录
            taskTimeArrayList.add(new IntervalMerging<LocalDateTime>().setBegin(s).setEnd(e));
            // 任务 起止里程 记录
            taskMileageArrayList.add(new IntervalMerging<BigDecimal>().setBegin(s1).setEnd(e1));
        }
        // 当前人员 的所有 任务 起止时间 并集 记录
        List<IntervalMerging<LocalDateTime>> taskTimeMerger = IntervalMerging.merger(taskTimeArrayList, LocalDateTime::compareTo);
        // 当前人员 的所有 任务 起止里程 并集 记录
        List<IntervalMerging<BigDecimal>> taskMileageMerger = IntervalMerging.merger(taskMileageArrayList, BigDecimal::compareTo);

        // 当前人员 在岗时长(min)
        Long duration = taskTimeMerger.stream()
                .map(taskTime -> LocalDateTimeUtil.between(taskTime.getBegin(), taskTime.getEnd()))
                .mapToLong(Duration::toMillis)
                .sum();
        System.out.println(duration);
        // 当前人员 任务里程(KM)
        BigDecimal mileage = taskMileageMerger.stream()
                .map(taskMileage -> NumberUtil.sub(taskMileage.getEnd(), taskMileage.getBegin()))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        System.out.println(mileage);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叁金Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值