我在特定的工作日有一组约会,从早上8点到下午6点:
任命1:上午9点至上午11点 预约2:下午2点至下午5点
我正在寻找一种有效的方式来找到空闲时间。所以在这种情况下,可用时间是:
8 am-9am 11 am-2pm 5时至6时
所以我有一个TimeBlock类
class TimeBlock {
public DateTime start
public DateTime end
}
var appointments = new List<TimeBlock>();
var freeTimeBlocks = new List<TimeBlock>();
' add appointments
appointments.Add(new TimeBlock{start...
appointments.Add(new TimeBlock{start...
我正在寻找一种有效的方法来寻找空闲时间,因为算法将在一个非常大的数据集上运行。
答案 0 :(得分:2)
确保时间块已排序(O(nlogn)
或更好),然后循环遍历它们并创建从每个块的末尾到下一个块的开头(O(n)
)的可用性范围。
答案 1 :(得分:2)
我认为这种方法对于大型数据集(d * u&gt;&gt; n)来说是渐近非常有效的,只要它们可以适合内存:
如果天数是d,用户数是u,每个用户每天的平均约会数是n,这是O(d * u * n),而基于排序的个人日方法会更像是O(d * u * n * log n)。
(如果u = 1或d = 1,则无关紧要 - 没有区别。)
创建一个由所有列表索引的列表数组 可能的时间段。例如,如果 你可以每5个时间段 分钟,你会有一个数组 大小120.这一步是O(1)。我们假设可能的时隙数是固定的,因此它应该与复杂性分析无关。
循环所有约会,所有天和所有用户,并添加约会(连同记录对于这两个约会开始和约会结束时间的相应列表。如果您使用链接列表并且每次都将其添加到列表的开头,则此步骤为O(d * u * n)。
创建一个数组来记录每一天和用户的“当前”空闲时段 - 您将在下一步中看到它的工作原理。
循环遍历第一个数组,并为数组中的每个列表循环遍历该列表(因此,循环是嵌套的)。对于每个约会,您看到这是“现在结束约会”,开始记录那个时间,当天和用户的新空闲时间段。对于每个约会,你看到这是一个“现在开始预约”,完成记录当天和用户的空闲时间段(如果有的话),如果它是零持续时间则丢弃它 - 如果没有,并且它不是8am,则创建一。这一步也是O(d * u * n)。
在下午6点完成所有空闲时间记录。这一步是最坏的情况O(d * u)。
无需进行预约间比较或搜索!
这是基于基数排序。
但是,我不确定这在实践中是否会更好 。它当然需要更多的空间!
答案 2 :(得分:1)
试试这个,它假定没有重叠的约会:
var orderedAppointments = appointments.OrderBy(a => a.start).ToArray();
freeTimeBlocks.Clear();
for(int i = 0; i < orderedAppointments.Length - 1; i++)
{
freeTimeBlocks.Add(new TimeBlock(){ start = orderedAppointments[i].end; end = orderedAppointments[i + 1].start });
}
var firstAppointment = orderedAppointments.First();
var lastAppointment = orderedAppointments.Last();
if(firstAppointment.start.Hour > 8)
freeTimeBlocks.Add(new TimeBlock() { start = firstAppointment.Today.AddHours(8); end = firstAppointment.start });
if(lastAppointment.end.Hour < 18)
freeTimeBlocks.Add(new TimeBlock() { start = lastAppointment.end; end = lastAppointment.Today.AddHours(18) });
答案 3 :(得分:1)
如果您没有任何重叠约会(从您的问题陈述中不可能),可能就像这样容易:
appointments.Sort((a,b) => a.Start.CompareTo(b.Start));
for(int i = 0; i< appointments.Count-1; i++)
{
if(appointments[i].End < appointments[i+1].Start)
freeTimeBlocks.Add(new TimeBlock() { Start = appointments[i].End, End = appointments[i+1].Start });
}
这不考虑第一次约会之前和最后约会之后的时间边缘情况,那些你必须手动检查和添加的情况。
答案 4 :(得分:1)
此答案还可以假设没有重叠的约会。此解决方案允许使用一个或多个可用时间段,而不是使用问题中给出的上午8点至下午6点的时间范围。
我认为这可以做得更优雅,但我不知道该怎么做。
private static List<TimeBlock> GetDayOpenSlots(IEnumerable<TimeBlock> daySlots,
IReadOnlyCollection<TimeBlock> usedDaySlots)
{
var freeTimeBlocks = new List<TimeBlock>();
foreach (var daySlot in daySlots)
{
var selectedSlotsWithinRange = usedDaySlots
.Where(x => x.StartTime >= daySlot.StartTime && x.EndTime <= daySlot.EndTime)
.OrderBy(x => x.StartTime)
.ToList();
if (!selectedSlotsWithinRange.Any())
{
freeTimeBlocks.Add(daySlot);
continue;
}
for (var i = 0; i < selectedSlotsWithinRange.Count - 1; i++)
{
var currentSlot = selectedSlotsWithinRange[i];
var nextSlot = selectedSlotsWithinRange[i + 1];
if (currentSlot.EndTime == nextSlot.StartTime)
{
continue;
}
freeTimeBlocks.Add(new TimeBlock(currentSlot.EndTime,
nextSlot.StartTime));
}
var firstAppointment = selectedSlotsWithinRange.First();
var lastAppointment = selectedSlotsWithinRange.Last();
if (firstAppointment.StartTime > daySlot.StartTime)
{
freeTimeBlocks.Add(new TimeBlock(daySlot.StartTime,
firstAppointment.StartTime));
}
if (lastAppointment.EndTime < daySlot.EndTime)
{
freeTimeBlocks.Add(new TimeBlock(lastAppointment.EndTime, daySlot.EndTime));
}
}
return freeTimeBlocks
.OrderBy(x => x.StartTime)
.ToList();
}