Java 最优雅方式校验时间段重叠

1. 时间段重叠校验问题

项目开发过程中经常碰到需要校验一连串时间段是否有重叠的问题,通常直观的解决方式是采用两层循环,从头至尾依次将集合中的时间段元素两两比较即可得出结果。这种方式虽然可行,但从实现上来看着实算不上优雅,只是能 work 而已。笔者花了一些时间分析,发现要校验时间段是否重叠其实只需要一点逆向思维,即只要列出两个时间段不重叠的情况,那么剩下的所有情形中二者必定有重叠。基于此,以下用两个时间段 segment1segment2 为例子进行分析:

时间段对象都有 startend 属性来标定一个时间范围,而时间是一维线性的,那么两个时间段的重叠与否其实只有以下 3 种情况:

  1. segment1.end < segment2.start ,不重叠
  2. segment1.start > segment2.end ,不重叠
  3. 其他情况,必定有重叠
/*
不重叠情况1:
                 segment1                    segment2
                /         \                 /         \
*    ----------|------------|--------------|-----------|-----
         start:2         end:5        start:9         end:15
*
*
不重叠情况2:
                 segment2                     segment1
                /         \                 /         \
*    ----------|------------|--------------|-----------|-----
         start:9         end:15        start:17        end:23
*
*/

2. 解决方案

经过上一节分析,3 种情况在笔者脑海中想到的处理方式是排序,于是最终有了以下方案:

  1. 时间段对象重写 Comparable#compareTo() 接口方法,在这个方法中定义比较规则,segment1 整体在 segment2 左侧返回 -1,segment1 整体在 segment2 右侧返回 1,其他情况返回 0
  2. 在时间段对象中添加重叠计数器 overlapCounter,当重写方法 Comparable#compareTo() 返回 0 时,计数器自增,记录当前时间段对象在与其他时间段对象比较排序过程中出现重叠的次数
  3. 使用 Java 8 的 Stream API 处理时间段集合,首先指定时间段对象重写的 Comparable#compareTo()Comparator 比较器实现,调用 Stream#sorted() 方法对时间段集合进行排序
  4. 经过排序操作,时间段对象的重叠计数器 overlapCounter 会将当前对象与集合中其他时间段对象的重叠计算结果记录下来,只要将集合中各个时间段对象的计数器累加,结果大于 0 即集合内有时间段出现重叠

  • 以上方案的关键在于采用 JDK 的排序比较,虽然其底层实现大概率也会有多层循环,但是一来 JDK 中的类都经过严格测试,通常较为可靠;二来使用 JDK 已有的 API, 也让代码更为简洁优雅

  • 除了使用 Integer 计数器记录重叠次数,其实也可以用集合来记录更多信息,比如与当前时间段对象重叠的其他时间段对象信息,这些可用于提示到底哪些时间段出现了重叠。总之,方案已经就绪,效果如何就看读者的想象力了

public class TimeSegment implements Comparable {

    private Integer start;

    private Integer end;

    private Integer overlapCounter = 0;

    public TimeSegment(Integer start, Integer end) {
        this.start = start;
        this.end = end;
    }

    public Integer getOverlapCounter() {
        return overlapCounter;
    }

    @Override
    public int compareTo(Object o) {
        TimeSegment other = (TimeSegment) o;
        if (this.end < other.start) {
            return -1;
        } else if (this.start > other.end) {
            return 1;
        }
        overlapCounter++;
        return 0;
    }

    public static void main(String[] args) {
        List<TimeSegment> list = new ArrayList<>();
        list.add(new TimeSegment(6, 9));
        list.add(new TimeSegment(1, 4));
        list.add(new TimeSegment(3, 5));

        long count = list.stream()
                .sorted(TimeSegment::compareTo)
                .mapToLong(TimeSegment::getOverlapCounter)
                .sum();
        System.out.println(count > 0);
    }
}
  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Java实现如下算法: 1.链表 链表用来存储数据,由一系列的结点组成。这些结点的物理地址不一定是连续的,即可能连续,也可能不连续,但链表里的结点是有序的。一个结点由数据的值和下一个数据的地址组成。一个链表内的数据类型可以是多种多样的。数组也是用来存储数据的,与链表相比,需要初始化时确定长度。一个数组内的数据都是同一类型。在Java中,ArrayList是通过数组实现,而LinkedList则通过链表实现。一个简单的链表类如下: 2.二叉树 二叉树是n(n>=0)个结点的有序集合。每个结点最多有2个子节点,即左结点和右结点,且左右结点顺序不能更改。 当n=0时,为空二叉树;当n=1时,为只有一个根二叉树。 3.排序 (1)冒泡排序 重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。时间复杂度 O(n2),为稳定算法。 将数依次进行比较,并将大(或小)的,网后放,如下: (2)快速排序 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 (3)选择排序 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。 (4)插入排序 每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。 每一个数和它前面的数依次进行比较,因为前面的数的顺序是已经排好的 (5)希尔排序 把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 (6)归并排序 建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。时间复杂度O(n log n) 。 (6)堆排序 利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值