C# 自定义类型的二进制搜索数组

C# 自定义类型的二进制搜索数组,c#,search,binary,custom-type,C#,Search,Binary,Custom Type,我有一个对象数组,每个对象都有一个公共字段值(double),它的随机双精度值介于0和1之间。A按此字段排序。我创建双随机=0.25。现在我想从具有[index].Value>=random的中查找第一个对象。我可以用int index=Array.BinarySearch()以某种方式完成这项工作吗?最好的方法是自己动手 public static class ListExtensions { public static T BinarySearchFirst<T>

我有一个对象数组,每个对象都有一个公共字段值(double),它的随机双精度值介于0和1之间。A按此字段排序。我创建双随机=0.25。现在我想从具有[index].Value>=random的中查找第一个对象。我可以用int index=Array.BinarySearch()以某种方式完成这项工作吗?

最好的方法是自己动手

public static class ListExtensions
{
        public static T BinarySearchFirst<T>(this IList<T> list, Func<T, int> predicate)
            where T : IComparable<T>
    {
        int min = 0;
        int max = list.Count;
        while (min < max)
        {
            int mid = (max + min) / 2;
            T midItem = list[mid];
            int comp = predicate(midItem);
            if (comp < 0)
            {
                min = mid + 1;
            }
            else if (comp > 0)
            {
                max = mid - 1;
            }
            else
            {
                return midItem;
            }
        }
        if (min == max &&
            predicate(list[min]) == 0)
        {
            return list[min];
        }
        throw new InvalidOperationException("Item not found");
    }
}

基于

这里是一个可以使用的二进制搜索的实现。除了通常可以接受的其他参数外,它还接受一个
选择器
,该选择器确定每个项应比较的实际对象,并且对于要查找的值,它接受该类型的值,而不是数组的类型

public static int BinarySearch<TSource, TKey>(this IList<TSource> collection
    , TKey item, Func<TSource, TKey> selector, Comparer<TKey> comparer = null)
{
    return BinarySearch(collection, item, selector, comparer, 0, collection.Count);
}
private static int BinarySearch<TSource, TKey>(this IList<TSource> collection
    , TKey item, Func<TSource, TKey> selector, Comparer<TKey> comparer
    , int startIndex, int endIndex)
{
    comparer = comparer ?? Comparer<TKey>.Default;

    while (true)
    {
        if (startIndex == endIndex)
        {
            return startIndex;
        }

        int testIndex = startIndex + ((endIndex - startIndex) / 2);
        int comparision = comparer.Compare(selector(collection[testIndex]), item);
        if (comparision > 0)
        {
            endIndex = testIndex;
        }
        else if (comparision == 0)
        {
            return testIndex;
        }
        else
        {
            startIndex = testIndex + 1;
        }
    }
}

假设他真的想线性搜索,从数组中的第一项开始。二元搜索让我相信他想比O(n)更快地得到答案。@RobertHarvey我忘了二元搜索部分,让我修改我的答案。听起来,既然你想要不精确匹配的第一项,二元搜索算法能做的最多就是分离出一个“足够小”的范围供你迭代,但是我可能弄错了。@AnthonyPegram你弄错了,二进制搜索正是他想要的,问题是他没有一个与数组类型相同的对象,他只有他想要比较的值。从逻辑上讲,二进制搜索可以工作,但他可能无法使用二进制搜索的
数组
实现。@Servy,你可能是对的,我在脑子里想清楚了。他必须在找到最初的匹配后继续搜索(即使是精确的),直到他确定是否已找到匹配的连续第一次出现。我在想,一旦找到任何匹配项,典型的二进制搜索就会很高兴地返回。(我注意到,我在算法领域非常不称职,因为我没有主修CS或以其他方式填补这些空白。)@AnthonyPegram
Array.BinarySearch
如果发现完全匹配,将返回一个索引,如果没有匹配,则返回给定项所属索引的按位补充,所以从结果中你已经知道它是否找到了精确的匹配。如果存在精确匹配,您可能确实需要添加一些特殊处理来支持数组中的重复项。
public static int BinarySearch<TSource, TKey>(this IList<TSource> collection
    , TKey item, Func<TSource, TKey> selector, Comparer<TKey> comparer = null)
{
    return BinarySearch(collection, item, selector, comparer, 0, collection.Count);
}
private static int BinarySearch<TSource, TKey>(this IList<TSource> collection
    , TKey item, Func<TSource, TKey> selector, Comparer<TKey> comparer
    , int startIndex, int endIndex)
{
    comparer = comparer ?? Comparer<TKey>.Default;

    while (true)
    {
        if (startIndex == endIndex)
        {
            return startIndex;
        }

        int testIndex = startIndex + ((endIndex - startIndex) / 2);
        int comparision = comparer.Compare(selector(collection[testIndex]), item);
        if (comparision > 0)
        {
            endIndex = testIndex;
        }
        else if (comparision == 0)
        {
            return testIndex;
        }
        else
        {
            startIndex = testIndex + 1;
        }
    }
}
public class Foo
{
    public double Value { get; set; }
}

private static void Main(string[] args)
{
    Foo[] array = new Foo[5];
    //populate array with values
    array.BinarySearch(.25, item => item.Value);
}