【问题标题】:Binary searching array of custom type自定义类型的二进制搜索数组
【发布时间】:2013-02-12 10:18:40
【问题描述】:

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

【问题讨论】:

  • 听起来,因为您想要 不精确 匹配的 first 项,二分查找算法所能做的最多就是隔离“足够小”范围供您迭代,但我可能弄错了。
  • @AnthonyPegram 你错了,二分搜索正是他想要的,问题是他没有与数组相同类型的对象,他只有他想要比较的值在。从逻辑上讲,二分搜索可以工作,只是他可能无法使用二分搜索的Array 实现。
  • @Servy,你可能是对的,我在脑子里想通了。他必须在找到初始匹配后继续搜索(即使是精确的),直到他满意是否找到了匹配的连续第一次出现。我在想,一旦找到任何匹配项,典型的二分搜索就会很高兴地返回。 (我注意到我在算法领域非常无能,没有学过 CS 专业,也没有填补这些空白。)
  • @AnthonyPegram Array.BinarySearch 如果找到完全匹配,将返回一个索引,如果没有匹配,则返回给定项目所属索引的按位补码,因此从结果中您已经知道是否它是否找到了完全匹配。如果存在完全匹配,您可能确实需要添加一些特殊处理来支持数组中的重复项。

标签: c# search binary custom-type


【解决方案1】:

这是您可以使用的BinarySearch 的实现。除了通常会被接受的其他参数之外,它还接受一个selector,它确定应该为每个项目比较的实际对象,并且对于要找到的值,它接受该类型的值,而不是类型数组。

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);
}

【讨论】:

    【解决方案2】:

    最好的办法是自己动手。

    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");
        }
    }
    

    用法:

    var list = Enumerable.Range(1, 25).ToList();
    var mid = list.Count / 2; //13
    
    list.BinarySearchFirst(c => c >= 23 ? 0 : -1); // 23
    

    基于Can LINQ use binary search when the collection is ordered?

    【讨论】:

    • 假设他真的想线性搜索,从数组中的第一项开始。二分搜索让我相信他想要比 O(n) 更快地得到答案。
    • @RobertHarvey 我忘了二分搜索部分让我修改我的答案。
    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    相关资源
    最近更新 更多