检查日期是否重叠并返回最大计数

如何解决检查日期是否重叠并返回最大计数

我有多个开始和结束的日期。 这些日期可能如下所示:

d1:      |----------|
d2:            |------|
d3:        |--------------|
d4:                         |----|
d5:   |----|

现在我需要检查重叠日期的最大计数。 所以在这个例子中,我们得到最多 3 个重叠日期(d1、d2、d3)。 考虑一下,可以有 0 到 n 个日期。

你能帮我完成这个任务吗? 提前致谢。

更新

输入:带有开始和结束点的 Java-Date 列表,例如 List,其中 MyCustomDate 包含开始和结束日期

输出: 重叠日期(作为 MyCustomDate 的列表)

每个时间跨度都包括一个带有小时和秒的 LocalDateTime 类型的开始和结束点。

解决方法

我的回答会考虑:

  • 给定 (d3,d5) 不重叠 => 重叠 (d1,d3,d5) = 2,因为在给定时间只有两个日期会重叠。
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

class Event {
    LocalDate startDate; // inclusive
    LocalDate endDate; // inclusive

    Event(LocalDate st,LocalDate end) {
        this.startDate = st;
        this.endDate = end;
    }

    // Getters & Setters omitted
}

public class Main {
    public static void main(String[] args) {
        List<Event> events = new ArrayList<Event>();
        events.add(new Event(LocalDate.of(2019,1,1),LocalDate.of(2019,5,1))); // d1
        events.add(new Event(LocalDate.of(2019,3,6,1))); // d2
        events.add(new Event(LocalDate.of(2019,2,7,1))); // d3
        events.add(new Event(LocalDate.of(2019,8,12,1))); // d4
        // d5 do not overlap d3
        events.add(new Event(LocalDate.of(2018,31))); // d5

        Integer startDateOverlaps = events.stream().map(Event::getStartDate).mapToInt(date -> overlap(date,events)).max().orElse(0);
        Integer endDateOverlaps = events.stream().map(Event::getEndDate).mapToInt(date -> overlap(date,events)).max().orElse(0);

        System.out.println(Integer.max(startDateOverlaps,endDateOverlaps));
    }

    public static Integer overlap(LocalDate date,List<Event> events) {
        return events.stream().mapToInt(event -> (! (date.isBefore(event.startDate) || date.isAfter(event.endDate))) ? 1 : 0).sum();
    }
}

我们对每个重叠日期求和(即使是它本身,否则 (d1,d2,d3) 只会计算 (d2,d3) 以进行 d1 检查)并测试每个 startDate 和 endDate。

,

您可以简单地为每个 startDate(按一天的粒度)生成 ​​endDateEvent 之间的所有事件并计算 Map,其中键是 {{1} }(作为单独的一天),value 是该日期出现的次数:

LocalDate
,

此时存在的其他 2 个答案都是 O(n2),对所有事件进行笛卡尔连接。这个答案展示了一种具有 O(n log n) 时间复杂度的替代方法。

我们所做的是构建一个有序的日期列表,并为每个日期注册在该日期开始和结束的范围。它可以存储为单个数字,例如如果范围结束 (-1) 并且 3 个范围开始 (+3),则日期的增量为 +2。

基本上,每个事件实际上是 2 个事件,一个开始事件和一个结束事件。

然后我们按日期顺序迭代列表,更新运行总数,并记住最大运行总数。

有几种编码方法。我将使用常规循环,而不是流,并且由于问题说每个日期都有一个小到毫秒的开始和结束点,我们将使用一个带有两个 DateRange 字段的 Instant 对象。

static int maxRangeOverlaps(List<DateRange> ranges) {
    Map<Instant,Delta> dateDelta = new TreeMap<>();
    for (DateRange range : ranges) {
        dateDelta.computeIfAbsent(range.getStart(),k -> new Delta()).value++;
        dateDelta.computeIfAbsent(range.getEnd(),k -> new Delta()).value--;
    }
    int total = 0,max = 0;
    for (Delta delta : dateDelta.values())
        if ((total += delta.value) > max)
            max = total;
    return max;
}
public final class DateRange {
    private final Instant start; // inclusive
    private final Instant end; // exclusive

    // Constructor and getter methods here
}
final class Delta {
    public int value;
}

测试

//       ....:....1....:....2....:....3
// d1:      |----------|
// d2:            |------|
// d3:        |--------------|
// d4:                         |----|
// d5:   |----|
List<DateRange> ranges = Arrays.asList(
        new DateRange(LocalDate.of(2021,4),LocalDate.of(2021,15)),new DateRange(LocalDate.of(2021,10),17)),6),21)),23),28)),6)));

System.out.println(maxRangeOverlaps(ranges)); // prints 3

通过添加一个辅助构造函数,上述测试被简化为使用 LocalDate 而不是 Instant

public DateRange(LocalDate start,LocalDate end) {
    this(start.atStartOfDay().toInstant(ZoneOffset.UTC),end.atStartOfDay().toInstant(ZoneOffset.UTC));
}
,

这个问题最初要求日期。发布答案后,问题更改为要求 LocalDateTime。我会留下这个答案,因为它 (a) 回答了最初发布的问题,并且 (b) 可能对其他人有帮助。


其他答案看起来很有趣并且可能是正确的。但我发现以下代码更易于遵循和验证/调试。

警告:我并不声称此代码是最好的、最精简的或最快的。坦率地说,我在这里的尝试只是为了突破我对使用 Java 流和 lambda 的理解的极限。

不要发明自己的类来保存开始/结束日期。 ThreeTen-Extra 库提供了一个 LocalDateRange 类来将附加到时间线的时间跨度表示为一对 java.time.LocalDate 对象。 LocalDateRange 提供了多种方法:

  • 比较,例如 abutsoverlaps
  • 工厂方法,例如 unionintersection

我们可以使用 Java 9 及更高版本中方便的 List.of 方法定义输入,以制作不可修改的 LocalDateRange 列表。

List < LocalDateRange > dateRanges =
        List.of(
                LocalDateRange.of( LocalDate.of( 2019,1 ),LocalDate.of( 2019,1 ) ),LocalDateRange.of( LocalDate.of( 2019,// Not connected to the others.
                LocalDateRange.of( LocalDate.of( 2018,31 ) )  // Earlier start,in previous year.
        );

确定所涉及日期的总体范围,即第一个开始和最后一个结束。

请记住,我们正在处理一个日期范围列表 (LocalDateRange),每个列表包含一对日期 (LocalDate) 对象。比较器比较存储在每个 LocalDate 中的起始/结束 LocalDateRange 对象,以获得最小值或最大值。此处看到的 get 方法正在获取 LocalDateRange,因此我们然后调用 getStart/getEnd 来检索存储在其中的开始/结束 LocalDate

LocalDate start = dateRanges.stream().min( Comparator.comparing( localDateRange -> localDateRange.getStart() ) ).get().getStart();
LocalDate end = dateRanges.stream().max( Comparator.comparing( localDateRange -> localDateRange.getEnd() ) ).get().getEnd();

列出该时间间隔内的所有日期。 LocalDate#datesUntil 方法生成在一对开始日期和结束日期之间找到的 LocalDate 对象流。开始是包容的,而结束是独占的。

List < LocalDate > dates =
        start
                .datesUntil( end )
                .collect( Collectors.toList() );

对于每个日期,获取包含该日期的日期范围列表。

Map < LocalDate,List < LocalDateRange > > mapDateToListOfDateRanges = new TreeMap <>();
for ( LocalDate date : dates )
{
    List < LocalDateRange > hits = dateRanges.stream().filter( range -> range.contains( date ) ).collect( Collectors.toList() );
    System.out.println( date + " ➡ " + hits );  // Visually interesting to see on the console.
    mapDateToListOfDateRanges.put( date,hits );
}

对于每个日期,获取包含该日期的日期范围的计数。我们想要对我们放入上面地图的每个 List 进行计数。在我的问题 Report on a multimap by producing a new map of each key mapped to the count of elements in its collection value 中讨论了生成一个新地图,其值为原始地图中集合的计数,我从 Answer by Syco 中提取了代码。

Map < LocalDate,Integer > mapDateToCountOfDateRanges =
        mapDateToListOfDateRanges
                .entrySet()
                .stream()
                .collect(
                        Collectors.toMap(
                                ( Map.Entry < LocalDate,List < LocalDateRange > > e ) -> { return e.getKey(); },( Map.Entry < LocalDate,List < LocalDateRange > > e ) -> { return e.getValue().size(); },( o1,o2 ) -> o1,TreeMap :: new
                        )
                );

不幸的是,似乎没有办法让流通过最大值过滤映射中的多个条目。请参阅:Using Java8 Stream to find the highest values from map

因此,首先我们要找到地图中一个或多个条目的值的最大值。

Integer max = mapDateToCountOfDateRanges.values().stream().max( Comparator.naturalOrder() ).get();

然后我们仅过滤具有该数字值的条目,将这些条目移动到新地图。

Map < LocalDate,Integer > mapDateToCountOfDateRangesFilteredByHighestCount =
        mapDateToCountOfDateRanges
                .entrySet()
                .stream()
                .filter( e -> e.getValue() == max )
                .collect(
                        Collectors.toMap(
                                Map.Entry :: getKey,Map.Entry :: getValue,TreeMap :: new
                        )
                );

转储到控制台。

System.out.println( "dateRanges = " + dateRanges );
System.out.println( "start/end = " + LocalDateRange.of( start,end ).toString() );
System.out.println( "mapDateToListOfDateRanges = " + mapDateToListOfDateRanges );
System.out.println( "mapDateToCountOfDateRanges = " + mapDateToCountOfDateRanges );
System.out.println( "mapDateToCountOfDateRangesFilteredByHighestCount = " + mapDateToCountOfDateRangesFilteredByHighestCount );

短期结果。

[警告:我没有手动验证这些结果。使用此代码风险自负,并自行验证。]

mapDateToCountOfDateRangesFilteredByHighestCount = {2019-03-01=3,2019-03-02=3,2019-03-03=3,2019-03-04=3,2019-03-05=3,-2019-03 06=3,2019-03-07=3,2019-03-08=3,2019-03-09=3,2019-03-10=3,2019-03-11=3,2019-03-12= 3,2019-03-13=3,2019-03-14=3,2019-03-15=3,2019-03-16=3,2019-03-17=3,2019-03-18=3,2019-03-19=3,2019-03-20=3,2019-03-21=3,2019-03-22=3,2019-03-23=3,2019-03-24=3,2019- 03-25=3,2019-03-26=3,2019-03-27=3,2019-03-28=3,2019-03-29=3,2019-03-30=3,2019-03- 31=3,2019-04-01=3,2019-04-02=3,2019-04-03=3,2019-04-04=3,2019-04-05=3,2019-04-06= 3,2019-04-07=3,2019-04-08=3,2019-04-09=3,2019-04-10=3,2019-04-11=3,2019-04-12=3,2019-04-13=3,2019-04-14=3,2019-04-15=3,2019-04-16=3,2019-04-17=3,2019-04-18=3,2019- 04-19=​​3,2019-04-20=3,2019-04-21=3,2019-04-22=3,2019-04-23=3,2019-04-24=3,2019-04- 25=3,2019-04-26=3,2019-04-27=3,2019-04-28=3,2019-04-29=3,2019-04-30=3}

完整代码

为了方便您复制粘贴,这里有一个完整的类来运行此示例代码。

package work.basil.example;


import org.threeten.extra.LocalDateRange;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

public class DateRanger
{
    public static void main ( String[] args )
    {
        DateRanger app = new DateRanger();
        app.demo();
    }

    private void demo ( )
    {
        // Input.
        List < LocalDateRange > dateRanges =
                List.of(
                        LocalDateRange.of( LocalDate.of( 2019,// Not connected to the others.
                        LocalDateRange.of( LocalDate.of( 2018,in previous year.
                );


        // Determine first start and last end.
        LocalDate start = dateRanges.stream().min( Comparator.comparing( localDateRange -> localDateRange.getStart() ) ).get().getStart();
        LocalDate end = dateRanges.stream().max( Comparator.comparing( localDateRange -> localDateRange.getEnd() ) ).get().getEnd();
        List < LocalDate > dates =
                start
                        .datesUntil( end )
                        .collect( Collectors.toList() );

        // For each date,get a list of the date-dateRanges containing that date.
        Map < LocalDate,List < LocalDateRange > > mapDateToListOfDateRanges = new TreeMap <>();
        for ( LocalDate date : dates )
        {
            List < LocalDateRange > hits = dateRanges.stream().filter( range -> range.contains( date ) ).collect( Collectors.toList() );
            System.out.println( date + " ➡ " + hits );  // Visually interesting to see on the console.
            mapDateToListOfDateRanges.put( date,hits );
        }

        // For each of those dates,get a count of date-ranges containing that date.
        Map < LocalDate,Integer > mapDateToCountOfDateRanges =
                mapDateToListOfDateRanges
                        .entrySet()
                        .stream()
                        .collect(
                                Collectors.toMap(
                                        ( Map.Entry < LocalDate,TreeMap :: new
                                )
                        );

        // Unfortunately,there seems to be no way to get a stream to filter more than one entry in a map by maximum value.
        // So first we find the maximum number in a value for one or more entries of our map.
        Integer max = mapDateToCountOfDateRanges.values().stream().max( Comparator.naturalOrder() ).get();
        // Then we filter for only entries with a value of that number,moving those entries to a new map.
        Map < LocalDate,Integer > mapDateToCountOfDateRangesFilteredByHighestCount =
                mapDateToCountOfDateRanges
                        .entrySet()
                        .stream()
                        .filter( e -> e.getValue() == max )
                        .collect(
                                Collectors.toMap(
                                        Map.Entry :: getKey,TreeMap :: new
                                )
                        );

        System.out.println( "dateRanges = " + dateRanges );
        System.out.println( "start/end = " + LocalDateRange.of( start,end ).toString() );
        System.out.println( "mapDateToListOfDateRanges = " + mapDateToListOfDateRanges );
        System.out.println( "mapDateToCountOfDateRanges = " + mapDateToCountOfDateRanges );
        System.out.println( "mapDateToCountOfDateRangesFilteredByHighestCount = " + mapDateToCountOfDateRangesFilteredByHighestCount );
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res