【问题标题】:Find a space of time in a set of dates在一组日期中找到一个时间空间
【发布时间】:2016-03-24 17:20:44
【问题描述】:

我来这里有一个问题我想分享,我希望任何人都可以帮助我解决这个问题。我将尝试尽可能清楚地描述问题。问题如下。

我有一个 java 程序,有一个接收一组日期 (java.util.Date) 的方法。

| start    end  |
| date1    date1|
<--------------->
|       | start        end  |     |                   |
|       | date2        date2|     |                   |
|       <------------------->     |                   |
|                                 | start        end  |
|                                 | date3        date3|
|                                 <------------------->

在上面的例子中,我们有三个日期,前两个相交,但 start-date3 在 end-date2 之后。对于我的业务规则,这是一个时间空间。

现在考虑下一个场景。

| start    end  |
| date1    date1|
<--------------->
|       | start        end  |     |                   |
|       | date2        date2|     |                   |
|       <------------------->     |                   |
|                                 | start        end  |
|                                 | date3        date3|
|                                 <------------------->   
|  |                                                      |
|  | start                                           end  |
|  | date4                                           date4|
|  <------------------------------------------------------>

在这种情况下,即使 end-date2 和 start-date3 之间有时间间隔,也认为它不存在时间间隔,因为 start-date4 和 end-date4 之间的时间覆盖了这个空间.

我想检索true,如果有一个或多个时间空间,否则我会返回false。

我尝试过的唯一方法是循环每个开始/结束关系,比较 end-date1 vs start-date2 vs start-date3 等等......这不是我想要应用的。

如果有其他想法,欢迎提出。如果您需要更多信息,我会添加它。谢谢。

【问题讨论】:

  • 第二种情况你有空间时间吗?如果是那么为什么,即使开始日期4覆盖了时空,那么你没有时空。
  • 相关问题,以及几个答案:stackoverflow.com/questions/32585990/…
  • @BahramdunAdil 在第二种情况下,即使在 end-date2 和 start-date3 之间确实存在时间间隔,我可以说这组日期没有空格,因为之间的时间范围start-date4 和 end-date4 与 end-date2 和 start-date3 之间的空间重叠。基本上,我正在寻找从最早的开始日期到最新的结束日期没有空格。如果还是不清楚,请追问,我会尽快回复的

标签: java algorithm sorting


【解决方案1】:

好吧,这确实是一个“奇怪”的想法,但看看这个“概念”是否更好。

基本上,这个想法是将所有重叠的日期尽可能合并到几个“范围”中,这意味着,在您的第一个示例中,您最终会得到两个不同的范围(开始日期 1 到结束日期 2 和 (开始日期 3 到结束日期 3),在第二个你会得到一个(开始日期 1 到结束日期 4)

所以,如果集合只有一个不同的范围,那么你就没有间隙,否则,你就有。

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

public class TestDateRanges {

    public static void main(String[] args) {
        try {
            List<Date[]> dates = new ArrayList<>(
                    Arrays.asList(
                            new Date[][]{
                                {makeDate("1.1.2015"), makeDate("5.1.2015")},
                                {makeDate("3.1.2015"), makeDate("10.1.2015")},
                                {makeDate("15.1.2015"), makeDate("20.1.2015")},}
                    )
            );

            Collections.sort(dates, new Comparator<Date[]>() {
                @Override
                public int compare(Date[] o1, Date[] o2) {
                    return o1[0].compareTo(o2[0]);
                }
            });

            List<Date[]> ranges = new ArrayList<>(dates.size());
            Date[] baseRange = null;
            for (Date[] range : dates) {
                if (baseRange == null) {
                    baseRange = range;
                    ranges.add(baseRange);
                } else if ((baseRange[0].before(range[0]) || baseRange[0].equals(range[0])) && (baseRange[1].after(range[0]) || baseRange[1].equals(range[0])) {
                    System.out.println("> Overlap " + format(baseRange) + " <-> " + format(range));
                    if (range[1].after(baseRange[1])) {
                        baseRange[1] = range[1];
                    }
                } else {
                    System.out.println("> Out of range " + format(baseRange) + " >-< " + format(range));
                    baseRange = range;
                    ranges.add(baseRange);
                }
            }

            System.out.println("Has " + ranges.size() + " distinct ranges");

            for (Date[] range : ranges) {
                System.out.println(format(range));
            }
        } catch (ParseException exp) {
            exp.printStackTrace();
        }
    }

    public static final DateFormat FORMAT = new SimpleDateFormat("d.M.yyyy");

    protected static final Date makeDate(String value) throws ParseException {
        return FORMAT.parse(value);
    }

    private static String format(Date[] baseRange) {
        return FORMAT.format(baseRange[0]) + "->" + FORMAT.format(baseRange[1]);
    }

    private static Date[] makeDateRange(String from, String to) throws ParseException {
        return new Date[]{makeDate(from), makeDate(to)};
    }

}

哪些输出...

> Overlap 1.1.2015->5.1.2015 <-> 3.1.2015->10.1.2015
> Out of range 1.1.2015->10.1.2015 >-< 15.1.2015->20.1.2015
Has 2 distinct ranges
1.1.2015->10.1.2015
15.1.2015->20.1.2015

现在,如果我将设置更改为...

List<Date[]> dates = new ArrayList<>(
        Arrays.asList(
                new Date[][]{
                    {makeDate("1.1.2015"), makeDate("5.1.2015")},
                    {makeDate("3.1.2015"), makeDate("10.1.2015")},
                    {makeDate("15.1.2015"), makeDate("20.1.2015")},
                    makeDateRange("2.1.2015", "25.1.2015")
                }
        )
);

它输出...

> Overlap 1.1.2015->5.1.2015 <-> 2.1.2015->25.1.2015
> Overlap 1.1.2015->25.1.2015 <-> 3.1.2015->10.1.2015
> Overlap 1.1.2015->25.1.2015 <-> 15.1.2015->20.1.2015
Has 1 distinct ranges
1.1.2015->25.1.2015

这只是一个概念性的想法,您应该注意,此示例将更改 dates 列表中的数据,因此您需要先确保您拥有数据的副本

【讨论】:

  • 谢谢。它似乎应用了重叠算法。只有两个cmets:1)此方法适用于开始结束日期exclusive,如果其他人需要与日期inclusive重叠,肯定需要修改一点“之前” " 和 "之后" 有条件的。 2) 我认为正确的格式是 new SimpleDateFormat("d.M.yyyy") ;)
  • 嗯,有一个很好的指标表明我需要更多的睡眠。有点想我需要重新检查我昨天所做的所有工作:P
【解决方案2】:

这个问题有一个非常简单的算法。

  1. 创建一个起始值数组和一个单独的结束值数组。
  2. 对两个数组进行排序(独立)。
  3. InRange 设置为 0。现在以合并顺序扫描两个数组;确保如果值相同,则在开始值之前使用相同的值执行所有结束值。对于扫描中的每个值:

    一个。如果它来自结束值数组:递减 InRange。如果 InRange 现在为 0,则您已找到“时间空间”的开始

    b.如果它来自起始值数组:如果 InRange 为 0,则您已找到“时间空间”的 end。无论如何,增加 InRange

上述算法是为半开区间设计的,其中终值实际上不包含在区间中。在日期的情况下,您应该假设开始日期实际上是该日期之前的午夜,而结束日期实际上是它之后的午夜(因此它与第二天的开始日期相同)。这会影响您按顺序扫描合并数组的方式。如果您的问题中的日期范围是包含,则您只需将每个结束日期加 1。

要清楚,在您的第二个示例中,两个数组将是:

  1. 开始日期 1,开始日期 4,开始日期 2,开始日期 3
  2. 结束日期 1,结束日期 2,结束日期 3,结束日期 4

第3步的处理顺序为:

  • 开始日期 1,开始日期 4,开始日期 2,结束日期 1,开始日期 3,结束日期 3,结束日期 4。

您实际上不必创建两个单独的排序数组。您可以对单个数组中的所有端点(作为端点)进行排序,在其中将每个数据标记为开始或结束。 (理想情况下,您希望确保结束 X 出现在相同 X 的开始 X 之前。否则,该算法偶尔会产生零长度的“时间空间”范围,您将不得不忽略它。)

【讨论】:

  • 在步骤 3 中,您的意思是在排序后扫描每个数组,一个一个吗? “合并订单”是什么意思?当我得到这组日期时,我不知道哪些重叠,我可以得到一组非常大的日期。这两个场景只是我可以从我的数据集中提取的一个小案例。
  • @kavrosis:我的意思是按排序顺序同时扫描两个数组。 (既然两个数组都是排序的,那么下一个元素一定是两个数组之一的下一个元素,所以单次比较就足以判断是哪个了。)答案最后的例子是不是很清楚?
  • 好的,我明白了。对于时间压力,我应用了重叠算法,但可以肯定这确实是一个非常好的解决方案。
猜你喜欢
  • 1970-01-01
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-15
  • 1970-01-01
  • 2012-05-27
相关资源
最近更新 更多