我有Booking
的列表,其中包含startDate
和endDate
。我必须找到预订方面最繁忙的一天。
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次。
我想到的解决方案是:
但这不是有效的解决方案。我怎样才能有效地找到最忙碌的一天?
答案 0 :(得分:2)
您可以将每个Booking
对象转换为数字行中的间隔,然后将该问题视为在数字行上找到最大间隔数所包含的点的问题。
您可以通过附加年,月和日值来将日期转换为数字,如下所示:
2016-10-12 -> 20161012
然后你可以跟随this technique。这是我做的,没有将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 :(得分:1)
算法有效 O(n),其中n是 minDate 和 maxDate 之间的天数
PS。 Patrick Parker在this post中提到的解决方案也有效,但它需要 O(m * log(m))时间,其中<strong> m 是范围数。因此,您应根据任务规范选择解决方案。
答案 2 :(得分:1)
我想指出一个可能让你朝着正确方向前进的财产。
最频繁的日子将是终点(开始日期或结束日期),或者它们将与终点绑定。
因此,如果能够在关联日期找到一天,您只需要考虑落在终点上的天数。
现在,您将如何可靠地增加每个终点的频率?按顺序处理。但是按照开始顺序处理开始和结束是不够的。开始和结束都必须按日期顺序考虑。
答案 3 :(得分:1)
嗯,这很难看,但如果您有stream
List
Booking
来完成
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)));
现在我们可以遍历列表,每次预订都会获得从start
到end
的所有日期:
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];
答案 4 :(得分:0)
对于每个{startDate, endDate}
记录生成两个元组{startDate,'start'}
,{endDate, 'end'}
。首先对这些日期进行排序,然后对第二个值进行排序,确保end
在start
之后。 (据我所知,间隔是包容性的,所以你不想在结束前一个预定的那天开始之前对结束预订进行折扣。)
然后按照上述顺序遍历元组,使用每个start
递增一个计数器,每个end
递减一次,并跟踪到目前为止的最大值。最大值是预订日期最多。
复杂性为O(n * log(n))。
答案 5 :(得分:0)
我不确定这是否是性能最佳的解决方案,但您只需创建一系列日期,检查频率,然后以最高频率返回:
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;
}
答案 6 :(得分:0)
虽然这可能不是最有效的方法,但除非您正在处理数百万个日期,否则这将非常快。我在下面的例子中添加了几千个日期,并且在我的系统上没多久。
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();
}
}