我在日期中有多个“从时间”和“到时间”。
例如:
from time | - to Time - |---- Date ---- | -- diff date --
-- 10:00 -- | -- 12:00 -- | 2019-08-07 | 2 Hours
-- 10:00 -- | -- 12:00 -- | 2019-08-07 | 2 Hours
-- 11:00 -- | -- 12:00 -- | 2019-08-07 | 1 Hours
-- 11:00 -- | -- 14:00 -- | 2019-08-07 | 3 Hours
-- 14:00 -- | -- 18:00 -- | 2019-08-07 | 4 Hours
-- 15:00 -- | -- 17:00 -- | 2019-08-07 | 2 Hours
-- 18:00 -- | -- 19:00 -- | 2019-08-07 | 1 Hours
上述时间总和是:15小时
但是它是错误的。因为有些时候是重复的。正确答案是9小时。
如何计算此问题的正确答案?
答案 0 :(得分:3)
这比您想象的要难,至少在一般情况下如此。
这是我用来计算数字范围并说明重叠区域的一类的修改版本(完整的类还可以处理排除的区域,这是我已包括但未用于此答案的区域):
public sealed class RangeCombiner
{
public void Include(long start, long end)
{
_boundaries.Add(new Boundary(start, isStart: true, isIncluded: true));
_boundaries.Add(new Boundary(end, isStart: false, isIncluded: true));
_sorted = false;
}
public void Exclude(long start, long end)
{
_boundaries.Add(new Boundary(start, isStart: true, isIncluded: false));
_boundaries.Add(new Boundary(end, isStart: false, isIncluded: false));
_sorted = false;
}
public void Clear()
{
_boundaries.Clear();
}
public long TotalIncludedRange()
{
sortIfNecessary();
return totalIncludedRange();
}
void sortIfNecessary()
{
if (_sorted)
return;
_boundaries.Sort();
_sorted = true;
}
long totalIncludedRange()
{
int included = 0;
int excluded = 0;
long start = 0;
long total = 0;
for (int i = 0; i < _boundaries.Count; ++i)
{
if (_boundaries[i].IsStart) // Starting a region...
{
if (_boundaries[i].IsIncluded) // Starting an included region...
{
if (++included == 1 && excluded == 0) // Starting a new included region,
start = _boundaries[i].Value; // so remember its start time.
}
else // Starting an excluded region...
{
if (++excluded == 1 && included > 0) // Ending an included region,
total += _boundaries[i].Value - start; // so add its range to the total.
}
}
else // Ending a region...
{
if (_boundaries[i].IsIncluded) // Ending an included region...
{
if (--included == 0 && excluded == 0) // Ending an included region,
total += _boundaries[i].Value - start; // so add its range to the total.
}
else // Ending an excluded region...
{
if (--excluded == 0 && included > 0) // Starting an included region,
start = _boundaries[i].Value; // so remember its start time.
}
}
}
return total;
}
readonly List<Boundary> _boundaries = new List<Boundary>();
bool _sorted;
struct Boundary : IComparable<Boundary>
{
public Boundary(long value, bool isStart, bool isIncluded)
{
Value = value;
IsStart = isStart;
IsIncluded = isIncluded;
}
public int CompareTo(Boundary other)
{
if (this.Value < other.Value)
return -1;
if (this.Value > other.Value)
return 1;
if (this.IsStart == other.IsStart)
return 0;
if (this.IsStart)
return -1;
return 1;
}
public readonly long Value;
public readonly bool IsStart;
public readonly bool IsIncluded;
}
}
这是解决问题的方法。请注意如何将DateTime
值转换为区域的滴答计数:
以下代码的输出为Total = 09:00:00
:
class Program
{
static void Main()
{
var combiner = new RangeCombiner();
var from1 = new DateTime(2019, 08, 07, 10, 00, 00);
var to1 = new DateTime(2019, 08, 07, 12, 00, 00);
var from2 = new DateTime(2019, 08, 07, 10, 00, 00);
var to2 = new DateTime(2019, 08, 07, 12, 00, 00);
var from3 = new DateTime(2019, 08, 07, 11, 00, 00);
var to3 = new DateTime(2019, 08, 07, 12, 00, 00);
var from4 = new DateTime(2019, 08, 07, 11, 00, 00);
var to4 = new DateTime(2019, 08, 07, 14, 00, 00);
var from5 = new DateTime(2019, 08, 07, 14, 00, 00);
var to5 = new DateTime(2019, 08, 07, 18, 00, 00);
var from6 = new DateTime(2019, 08, 07, 15, 00, 00);
var to6 = new DateTime(2019, 08, 07, 17, 00, 00);
var from7 = new DateTime(2019, 08, 07, 18, 00, 00);
var to7 = new DateTime(2019, 08, 07, 19, 00, 00);
combiner.Include(from1.Ticks, to1.Ticks);
combiner.Include(from2.Ticks, to2.Ticks);
combiner.Include(from3.Ticks, to3.Ticks);
combiner.Include(from4.Ticks, to4.Ticks);
combiner.Include(from5.Ticks, to5.Ticks);
combiner.Include(from6.Ticks, to6.Ticks);
combiner.Include(from7.Ticks, to7.Ticks);
Console.WriteLine("Total = " + TimeSpan.FromTicks(combiner.TotalIncludedRange()));
}
}
复杂度:
答案 1 :(得分:0)
考虑到LINQ的算法(请注意,我不写C#):
HashSet<T> foo = new HashSet<T>(from x in bar.Items select x)
; 没有LINQ,IMO会更容易。
答案 2 :(得分:0)
以下是有效代码:
https://dotnetfiddle.net/myhStL
这将为您提供每个不同日期的总小时数列表。
List<DateTime> dates = new List<DateTime>(){
new DateTime(2019,1,1,10,0,0),
new DateTime(2019,1,1,12,0,0),
new DateTime(2019,1,1,13,0,0),
new DateTime(2019,1,1,14,0,0),
new DateTime(2019,1,2,10,0,0),
new DateTime(2019,1,2,12,0,0),
new DateTime(2019,1,2,14,0,0),
new DateTime(2019,1,3,10,0,0),
new DateTime(2019,1,3,11,0,0),
new DateTime(2019,1,3,12,0,0)
};
var result = dates
.OrderBy(d => d.Date)
.ThenBy(d => d.TimeOfDay)
.GroupBy(d => d.Date)
.Select(bla => new
{
Date = bla.First().Date,
Hours = bla.Last() - bla.First()
}).ToList();
结果:
日期:1/1/2019 12:00:00 AM营业时间:04:00:00
日期:1/2/2019 12:00:00 AM营业时间:04:00:00
日期:1/3/2019 12:00:00 AM时间:02:00:00