【问题标题】:Binary search of a C# list using delegate condition使用委托条件对 C# 列表进行二进制搜索
【发布时间】:2010-10-05 00:21:37
【问题描述】:

我有一个List<T>,我想搜索的不是给定项目,而是满足给定条件的项目。给定列表中的一个项目,我可以测试 4 个条件中的哪一个为真:

  • 所需的项目必须在左侧
  • 所需的项目必须在右侧
  • 这是想要的物品
  • 想要的不能在列表中

快速浏览列表功能并不令人鼓舞,所以我想知道是否有人知道我可以使用的功能?

编辑:这是一个本地临时列表,所以我知道它会被正确排序

编辑:BinarySearch 看起来几乎正确,但在我的情况下,我没有可比较的项目。我会使用 Jon Skeet 的解决方案并忽略一个 arg,但我不确定我是否可以指望它始终是相同的 arg。

【问题讨论】:

    标签: c# search list


    【解决方案1】:

    新编辑:我将在下面留下额外的二进制搜索,因为它们对其他人有用,这是我认为您真正想要的最后一个选项。如果它正在寻找的项目“小于”指定的项目,您的代理应该返回一个正数,如果它“大于”指定的项目,则返回一个负数,如果它是正确的,则返回 0。

    public static int BinarySearchForMatch<T>(this IList<T> list,
        Func<T,int> comparer)
    {
        int min = 0;
        int max = list.Count-1;
    
        while (min <= max)
        {
            int mid = (min + max) / 2;
            int comparison = comparer(list[mid]);
            if (comparison == 0)
            {
                return mid;
            }
            if (comparison < 0)
            {
                min = mid+1;
            }
            else
            {
                max = mid-1;
            }
        }
        return ~min;
    }
    

    旧编辑:我将在下面留下原来的答案,但这里有两个其他选项。

    第一个从源数据投影到键类型,并指定要查找的键。比较本身只是以该键类型的默认方式完成:

    public static int BinarySearchBy<TSource,TKey>(this IList<TSource> list, 
        Func<TSource,TKey> projection, TKey key)
    {
        int min = 0;
        int max = list.Count-1;
    
        while (min <= max)
        {
            int mid = (min + max) / 2;
            TKey midKey = projection(list[mid]);
            int comparison = Comparer<TKey>.Default.Compare(midKey, key);
            if (comparison == 0)
            {
                return mid;
            }
            if (comparison < 0)
            {
                min = mid+1;
            }
            else
            {
                max = mid-1;
            }
        }
        return ~min;
    }
    

    第二个采用 Func 代替,将列表中的项目与我们正在寻找的键进行比较。当然,代码几乎完全相同 - 只是比较发生了变化:

    public static int BinarySearchBy<TSource,TKey>(this IList<TSource> list, 
        Func<TSource,TKey,int> comparer, TKey key)
    {
        int min = 0;
        int max = list.Count-1;
    
        while (min <= max)
        {
            int mid = (min + max) / 2;
            int comparison = comparer(list[mid], key);
            if (comparison == 0)
            {
                return mid;
            }
            if (comparison < 0)
            {
                min = mid+1;
            }
            else
            {
                max = mid-1;
            }
        }
        return ~min;
    }
    

    这些都未经测试,但至少可以编译:)

    原答案:

    您可以将List&lt;T&gt;.BinarySearchIComparer&lt;T&gt; 一起使用。您不必编写自己的IComparer&lt;T&gt; 实现——我已经在MiscUtil 中继续,它从Comparison&lt;T&gt; 委托构建IComparer&lt;T&gt;。这只会发现前三个条件,但二分查找会从其余条件中找出最后一个。

    事实上,代码太短了,我不妨把它贴在这里(诚然,没有 cmets)。

    public sealed class ComparisonComparer<T> : IComparer<T>
    {
        readonly Comparison<T> comparison;
    
        public ComparisonComparer(Comparison<T> comparison)
        {
            if (comparison == null)
            {
                throw new ArgumentNullException("comparison");
            }
            this.comparison = comparison;
        }
    
        public int Compare(T x, T y)
        {
            return comparison(x, y);
        }
    }
    

    所以你可能会这样做:

    var comparer = new ComparisonComparer<Person>((p1, p2) => p1.ID.CompareTo(p2.ID));
    int index = list.BinarySearch(employee, comparer);
    

    MiscUtil 还有一个您可能感兴趣的 ProjectionComparer - 您只需指定一个投影,就像在带有 LINQ 的 OrderBy 中一样 - 但它实际上取决于您的用例。

    【讨论】:

    • 我喜欢你的最新回答。我认为其中有一个错字 T TSource
    • 嗨 Jon,你能解释一下为什么在方法结束时返回 ~min 吗?我不太确定在这种情况下翻转位的意义是什么。
    • @NathanRidley:List 等中的 BinarySearch 方法旨在返回元素的索引(如果它存在),或者返回它插入的索引的按位倒数.有关更多详细信息,请参阅List&lt;T&gt;.BinarySearch 的文档 :)
    【解决方案2】:

    您可能希望在对象的自定义比较器中实现它(在 C# 文档中奇怪地称为比较器)。

    【讨论】:

    • 执行此操作的代码与执行完整搜索的代码大致相同。
    【解决方案3】:

    你确定这些条件吗?通常,如果列表已排序,则可以使用,在这种情况下,您可能希望首先使用 SortedList&lt;TKey, TValue&gt;

    无论哪种方式,假设 C# 3.0 或更高版本您可能需要.Where() 方法。将您的条件写成 Lambda 并让框架进行搜索。它应该使用适合该集合的搜索技术。

    【讨论】:

    • 因为没有key(value就是key)SortedList就是Award。
    • 线性扫描在哪里 - 它无法知道您要查找的内容与任何当前的排序顺序。
    • 代码回复:Lambda(我什至从未/看过/ LINQ。)
    • @BCS:你真的应该这样做。很可爱:)
    猜你喜欢
    • 2015-10-31
    • 1970-01-01
    • 2013-09-29
    • 2016-05-27
    • 2017-07-06
    • 1970-01-01
    • 2010-09-18
    • 2017-08-17
    • 1970-01-01
    相关资源
    最近更新 更多