繁体   English   中英

给定日期范围列表,找到发生最多次数的日期

[英]Given a list of date ranges, find a date which occurs maximum times

我有一个包含startDateendDateBooking列表。 我必须找到预订方面最繁忙的一天。

class Booking {
    Date startDate;
    Date endDate;
}

例:

2016-10-12 to 2016-10-18
2016-10-11 to 2016-10-15
2016-10-13 to 2016-10-14
2016-10-12 to 2016-10-13

从这些日期开始,很明显2016-10-13全部被预订了4次。

我想到的解决方案是:

  • 遍历列表中从最小startDate到最大endDate的所有日期
  • 记录所有日期的所有预订数量。
  • 最后,返回具有最大计数的日期。

但这不是有效的解决方案。 我怎样才能有效地找到最忙碌的一天?

您可以将每个Booking对象转换为数字行中的间隔,然后将该问题视为在数字行上找到最大间隔数所包含的点的问题。

您可以通过附加年,月和日值来将日期转换为数字,如下所示:

2016-10-12 -> 20161012

然后你可以按照这种技术 这是我做的,没有将Date转换为int

class Main {
    public static int findBusiest (int[] starts, int[] ends) {
        Arrays.sort(starts);
        Arrays.sort(ends);
        int n = starts.length;

        int in = 1, max = 1, time = starts[0];
        int i = 1, j = 0;

        while (i < n && j < n) {
            if (starts[i] <= ends[j]) {
                in++;

                if (in > max) {
                    max = in;
                    time = starts[i];
                }
                i++;
            }
            else {
                in--;
                j++;
            }
        }

        return time;
    }

    public static void main(String[] args) {

        int[] starts = { 20161012, 20161011, 20161013, 20161012 };
        int[] ends = { 20161018, 20161015, 20161014, 20161013 };

        System.out.println(findBusiest(starts, ends));
    }
}

输出:

20161013
  1. 为简单起见,给每个日期他们的索引(例如最小日期有索引0,第二天1等等),并初始化填充零的数组
  2. 遍历所有范围和数组中的开始日期索引增量元素,并减少结束日期。 (例如,如果某个日期d作为范围的开始满足3倍,而作为范围结束则满足5次,则应该为-2)
  3. 现在,遍历数组开头的所有日期,并将当前元素添加到您的计数器(基本上,日期d处的计数器值是它在范围内的频率)
  4. 答案是最大计数器值

算法起作用O(n) ,其中n是minDatemaxDate之间的天数

PS。 帕特里克帕克在这篇文章中提到的解决方案也有效,但它需要O(m * log(m))时间,其中m是范围的数量。 因此,您应根据任务规范选择解决方案。

我想指出一个可能让你朝着正确方向前进的财产。

最常见的一天将是端点(开始或结束日期),或者它们将与端点绑定。

因此,如果在绑定的日子里找到一天就足够了,您只需要考虑落在端点上的天数。

现在,您将如何可靠地增加每个终点的频率? 按顺序处理。 但是按照开始顺序处理开始和结束是不够的。 开始和结束都必须按日期顺序考虑。

嗯,这很难看,但如果您有Booking List ,我们可以使用stream

class Booking {
      LocalDate start;
      LocalDate end;
}

我们有一个List

List<Booking> bookings = new ArrayList<>();
bookings.add(new Booking(LocalDate.of(2016, 10, 12),LocalDate.of(2016, 10, 18)));
bookings.add(new Booking(LocalDate.of(2016, 10, 11),LocalDate.of(2016, 10, 15)));
bookings.add(new Booking(LocalDate.of(2016, 10, 13),LocalDate.of(2016, 10, 14)));
bookings.add(new Booking(LocalDate.of(2016, 10, 12),LocalDate.of(2016, 10, 13)));

现在我们可以遍历列表,并为每个预订获取从startend所有日期:

Stream<LocalDate> dateRanges = bookings.stream().flatMap(booking ->
        Stream.iterate(booking.start, d -> d.plusDays(1))
                .limit(ChronoUnit.DAYS.between(booking.start, booking.end) + 1)
);

我们有所有日期,让我们计算每个日期在新流中出现的次数。

Map<LocalDate, Long> datesFrequency = dateRanges.peek(System.out::println).
collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

最后让我们找到格言 - 最常见的日期:

Optional<Map.Entry<LocalDate, Long>> mostFrequent = datesFrequency.entrySet().
stream().max((o1, o2) -> o1.getValue().compareTo(o2.getValue()));

在这种情况下,结果将是可选的[2016-10-13 = 4];

对于每个{startDate, endDate}记录生成两个元组{startDate,'start'}{endDate, 'end'} 这些排序上的日期第一个,第二个值第二,确保end后去start (据我所知,间隔是包容性的,所以你不想在结束前一个预定的那天开始之前对结束预约进行折扣)。

然后用在顺序元组去,如上所述,递增与每个计数器start ,每个递减end ,和跟踪最大的为止。 最大值是预订日期最多。

复杂度为O(n * log(n))。

我不确定这是否是最高性能的解决方案,但您可以简单地创建一系列日期,检查频率,并以最高频率返回:

public static Date getMaxOccurence(Booking[] booking) {
    List<Date> dates = new ArrayList<Date>();
    Date max = new Date();
    int freq = 0;
    for (Booking b : booking) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(b.getStartDate());

        while (calendar.getTime().before(b.getEndDate())) {
            Date result = calendar.getTime();
            dates.add(result);
            int curr = Collections.frequency(dates, result);
            if (curr > freq) {
                freq = curr;
                max = result;
            }
            calendar.add(Calendar.DATE, 1);
        }
    }
    return max;
}

ideone演示

虽然这可能不是最有效的方法,但除非您正在处理数百万个日期,否则这将非常快。 我在下面的例子中添加了几千个日期,并且在我的系统上没多久。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;

public class Days {
    Days() {
        long startTime = System.nanoTime();
        SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd");
        ArrayList<Booking> dates = new ArrayList<Booking>();
        try {
            dates.add(new Booking(ft.parse("2016-10-14"), ft.parse("2016-10-18")));
            dates.add(new Booking(ft.parse("2016-11-11"), ft.parse("2018-12-15")));
            dates.add(new Booking(ft.parse("2016-10-13"), ft.parse("2016-10-14")));
            dates.add(new Booking(ft.parse("2016-10-5"), ft.parse("2016-10-13")));
            dates.add(new Booking(ft.parse("2016-10-12"), ft.parse("2020-10-13")));
            dates.add(new Booking(ft.parse("2016-10-10"), ft.parse("2018-10-13")));
            dates.add(new Booking(ft.parse("2015-10-12"), ft.parse("2016-11-13")));
            dates.add(new Booking(ft.parse("2016-09-12"), ft.parse("2016-12-13")));
            dates.add(new Booking(ft.parse("2016-08-12"), ft.parse("2016-10-18")));
            dates.add(new Booking(ft.parse("2016-10-01"), ft.parse("2016-10-26")));
            dates.add(new Booking(ft.parse("2016-02-03"), ft.parse("2016-10-28")));
            dates.add(new Booking(ft.parse("2016-10-04"), ft.parse("2016-12-28")));
            dates.add(new Booking(ft.parse("2015-10-05"), ft.parse("2056-10-16")));
            dates.add(new Booking(ft.parse("2012-10-12"), ft.parse("2016-10-14")));
            dates.add(new Booking(ft.parse("2011-10-12"), ft.parse("2017-02-18")));
            dates.add(new Booking(ft.parse("2016-10-06"), ft.parse("2018-10-13")));
            dates.add(new Booking(ft.parse("2016-10-12"), ft.parse("2019-10-13")));
        } catch (ParseException e) {
            e.printStackTrace();
        }

        ArrayList<Date> datesMult = new ArrayList<Date>();

        for(Booking b : dates) {
            datesMult.addAll(b.getDates());
        }

        HashSet<Date> datesOnce = new HashSet<Date>(datesMult);

        int highestCount = -1;
        Date mostUsed = new Date();

        for(Date d : datesOnce) {
            int count = Collections.frequency(datesMult, d);
            if(count > highestCount) {
                highestCount = count;
                mostUsed = d;
            }
        }

        System.out.println("The most common used date is " + ft.format(mostUsed) + " and it got used " + highestCount + " times");
        long endTime = System.nanoTime();

        long duration = (endTime - startTime);
        System.out.println("This took " + duration + " nanoseconds");
    }

    private class Booking {
        Date startDate;
        Date endDate;

        Booking(Date d1, Date d2) {
            startDate = d1;
            endDate = d2;
        }

        public ArrayList<Date> getDates() {
            ArrayList<Date> d = new ArrayList<Date>();
            d.add(startDate);
            Calendar c = Calendar.getInstance();
            while(true) {
                c.setTime(startDate);
                c.add(Calendar.DATE, 1);  // number of days to add
                startDate = c.getTime();
                if(startDate.compareTo(endDate) == -1) {
                    d.add(startDate);
                } else if(startDate.compareTo(endDate) == 0) {
                    d.add(endDate);
                    return d;
                }
            }
        }
    }

    public static void main(String[] args) {
        new Days();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM