我有一个范围列表,例如:(0,1),(1,4),(4,8),(8,14),(14,20),
Range是Class的类,包括Start&结束。
我可以应用BinarySearch搜索包含值10的段吗?
还有其他任何方法可以实现吗?
答案 0 :(得分:1)
您可以将IComparer界面与BinarySearch
一起使用此致
答案 1 :(得分:1)
查询范围集合的一般且最有效的方法是实现Interval Tree。但是,由于您的间隔从不重叠,因此您可以使用binary search approach。
所以,这就是我要做的事。
考虑Range
类实现如下:
class Range
{
public int Start { get; private set; }
public int End { get; private set; }
public Range(int start, int end)
{
this.Start = start;
this.End = end;
}
}
我们为Range
类创建了一个只考虑Start
属性的比较器(它已经足够了,因为我们假设没有重叠):
class StartOnlyRangeComparer : IComparer<Range>
{
public int Compare(Range x, Range y)
{
return x.Start.CompareTo(y.Start);
}
}
然后,这是包含在扩展类中的最重要的方法:
static class Extensions
{
/// <summary>
/// N.B. ranges must be sorted by ascending Start, must contain
/// non overlapping ranges and ranges with Start=End are not allowed.
/// </summary>
/// <param name="ranges"></param>
/// <param name="point"></param>
/// <returns></returns>
public static Range FindRangeContainingPoint(
this IList<Range> ranges, int point)
{
if (ranges.Count == 0)
return null;
var index = ranges.FindFirstIndexGreaterThanOrEqualTo(
new Range(point, point + 1),
new StartOnlyRangeComparer());
if (index < 0)
return null; // no existing range contains the point,
// if we wanted to insert a Range(point, point+1)
// would be before the first element
if (index >= ranges.Count)
{
var lastElement = ranges[ranges.Count - 1];
if (lastElement.ContainsPoint(point))
return lastElement;
else
return null; // no existing range contains the point,
// if we wanted to insert a Range(point, point+1)
// would be after the last element
}
if (ranges[index].ContainsPoint(point))
return ranges[index];
else if (index > 0 && ranges[index - 1].ContainsPoint(point))
return ranges[index - 1];
else
return null; // no existing range contains the point,
// if we wanted to insert a Range(point, point+1)
// would be at index - 1
}
/// <summary>
/// Lower Bound function on sorted list (see credits)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sortedCollection"></param>
/// <param name="key"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static int FindFirstIndexGreaterThanOrEqualTo<T>(
this IList<T> sortedCollection,
T key,
IComparer<T> comparer)
{
int begin = 0;
int end = sortedCollection.Count;
while (end > begin)
{
int index = begin + ((end - begin) / 2);
T el = sortedCollection[index];
if (comparer.Compare(el, key) >= 0)
end = index;
else
begin = index + 1;
}
return end;
}
/// <summary>
/// Check if point is included in [Start, End)
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public static bool ContainsPoint(this Range range, int point)
{
return range.Start <= point && range.End > point;
}
}
用法示例:
class Program
{
static void Main(string[] args)
{
var list = new List<Range>()
{
new Range(0, 1),
new Range(1, 4),
new Range(4, 8),
new Range(8, 14),
new Range(14, 20),
new Range(22, 24),
};
// yes I know in this case they're already sorted...
// but I want to stress the fact that you must sort the list
list.Sort(new StartOnlyRangeComparer());
var range10 = list.FindRangeContainingPoint(10); // returns (8,14)
var range21 = list.FindRangeContainingPoint(21); // returns null
var range20 = list.FindRangeContainingPoint(20); // returns null
var range14 = list.FindRangeContainingPoint(14); // return (14,20)
}
}
请注意,排除Start
边缘时会包含End
边缘(例如,请参阅range20
示例)。
FindFirstIndexGreaterThanOrEqualTo
方法的this SO post的积分
答案 2 :(得分:0)
我写了自己的功能:
public Range BinarySearch(double position)
{
Range part = null;
int from = 0;
int to = Count - 1;
while (from < to)
{
int middle = (from + to) / 2;
part = this[middle];
switch (part.CompareTo(position))
{
case 1:
from = Math.Min(middle + 1, to);
break;
case -1:
to = Math.Max(middle - 1, from);
break;
case 0:
from = to = middle;
break;
}
}
return this[to];
}