Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 如何在IList上执行二进制搜索<;T>;?_.net_Generics_List_Interface_Binary Search - Fatal编程技术网

.net 如何在IList上执行二进制搜索<;T>;?

.net 如何在IList上执行二进制搜索<;T>;?,.net,generics,list,interface,binary-search,.net,Generics,List,Interface,Binary Search,简单的问题-给定一个IList如何执行二进制搜索,而无需自己编写方法,也无需将数据复制到具有内置二进制搜索支持的类型。我目前的状态如下 List.BinarySearch()不是IList的成员 对于列表 IList不继承自IList,因此无法使用ArrayList.Adapter() 我倾向于相信内置方法不可能做到这一点,但我不能相信BCL/FCL中缺少这样一种基本方法 如果不可能,谁能为IList提供最短、最快、最智能或最漂亮的二进制搜索实现 更新 我们都知道,在使用二进制搜索之前,必须

简单的问题-给定一个
IList
如何执行二进制搜索,而无需自己编写方法,也无需将数据复制到具有内置二进制搜索支持的类型。我目前的状态如下

  • List.BinarySearch()
    不是
    IList的成员
  • 对于
    列表
  • IList
    不继承自
    IList
    ,因此无法使用
    ArrayList.Adapter()
我倾向于相信内置方法不可能做到这一点,但我不能相信BCL/FCL中缺少这样一种基本方法

如果不可能,谁能为
IList
提供最短、最快、最智能或最漂亮的二进制搜索实现

更新

我们都知道,在使用二进制搜索之前,必须对列表进行排序,因此您可以假设它是。但我假设(但未验证)排序也存在同样的问题-如何对IList进行排序

结论


对于
IList
,似乎没有内置的二进制搜索。可以使用
First()
OrderBy()
LINQ方法进行搜索和排序,但它可能会影响性能。您自己实现它(作为一个扩展方法)似乎是最好的选择。

在对
IList
进行二进制搜索时会遇到一些问题,首先,正如您所提到的,
列表上的
BinarySearch
方法不是
IList
接口的成员。其次,您无法在搜索之前对列表进行排序(这是二进制搜索必须执行的操作)


我认为最好的办法是创建一个新的
列表
,对其进行排序,然后进行搜索。它并不完美,但是如果你有一个
IList

我怀疑.NET中是否有这样一个通用的二进制搜索方法,除了一些基类中存在的方法(但显然不在接口中),那么就没有太多选项了,所以这里是我的通用方法

public static Int32 BinarySearchIndexOf<T>(this IList<T> list, T value, IComparer<T> comparer = null)
{
    if (list == null)
        throw new ArgumentNullException(nameof(list));

    comparer = comparer ?? Comparer<T>.Default;

    Int32 lower = 0;
    Int32 upper = list.Count - 1;

    while (lower <= upper)
    {
        Int32 middle = lower + (upper - lower) / 2;
        Int32 comparisonResult = comparer.Compare(value, list[middle]);
        if (comparisonResult == 0)
            return middle;
        else if (comparisonResult < 0)
            upper = middle - 1;
        else
            lower = middle + 1;
    }

    return ~lower;
}
public static Int32二进制搜索索引of(此IList列表,T值,IComparer comparer=null)
{
if(list==null)
抛出新ArgumentNullException(nameof(list));
比较器=比较器??比较器。默认值;
Int32下限=0;
Int32上限=list.Count-1;

while(lower请注意,虽然List和IList没有BinarySearch方法,但SortedList有。

如果可以使用.NET 3.5,则可以使用内置Linq扩展方法:

using System.Linq;

IList<string> ls = ...;
var orderedList = ls.OrderBy(x => x).ToList();
orderedList.BinarySearch(...);
使用System.Linq;
IList ls=。。。;
var orderedList=ls.OrderBy(x=>x.ToList();
orderedList.BinarySearch(…);

然而,对于Andrew Hare的解决方案来说,这只是一种稍微不同的方式,只有在您从同一个有序列表中多次搜索时,这才是真正有用的。

我喜欢使用扩展方法的解决方案。不过,有一点警告是必要的

这实际上是Jon Bentley在其著作《编程珍珠》(Programming Pearls)中的实现,它受到了一个20年左右未被发现的数字溢出错误的影响如果IList中有大量项,则可能导致Int32溢出。解决此问题的一个方法是使用减法来稍微不同地执行中间计算;middle=Lower+(Upper-Lower)/2

Bentley在《Pearls编程》中还警告说,虽然二进制搜索算法于1946年发布,第一个正确的实现直到1962年才发布


如果您需要在
IList
s(在
Algorithms.cs
中)上进行二进制搜索的现成实现:

//
///通过二进制搜索在已排序列表中搜索项目。必须对列表进行排序
///通过类型的自然排序(它是IComparableT的实现)。
/// 
///要搜索的已排序列表。
///要搜索的项目。
///返回可在其中找到项的第一个索引。如果
///值为零,表示列表中不存在,则
///返回可以插入以维护排序结果的索引
///清单的顺序。
///与列表中显示的项目数相等的项目数。
公共静态int二进制搜索(IList列表、T项、out int索引)
其中T:i可比较
{
// ...
}

请记住,对于某些列表实现,二进制搜索可能效率很低。例如,对于链表,如果正确实现,则为O(n),而O(n log n)如果您天真地实现它。

这是我的Lasse代码版本。我发现能够使用lambda表达式来执行搜索非常有用。在对象列表中搜索时,它只允许传递用于排序的键。使用IComparer的实现很小地派生自此

我还喜欢在找不到匹配项时返回~lower。Array.BinarySearch会这样做,它允许您知道您搜索的项目应该插入到哪里,以便保持顺序

/// <summary>
/// Performs a binary search on the specified collection.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <typeparam name="TSearch">The type of the searched item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value
/// with the list items.</param>
/// <returns></returns>
public static int BinarySearch<TItem, TSearch>(this IList<TItem> list,
    TSearch value, Func<TSearch, TItem, int> comparer)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }
    if (comparer == null)
    {
        throw new ArgumentNullException("comparer");
    }

    int lower = 0;
    int upper = list.Count - 1;

    while (lower <= upper)
    {
        int middle = lower + (upper - lower) / 2;
        int comparisonResult = comparer(value, list[middle]);
        if (comparisonResult < 0)
        {
            upper = middle - 1;
        }
        else if (comparisonResult > 0)
        {
            lower = middle + 1;
        }
        else
        {
            return middle;
        }
    }

    return ~lower;
}

/// <summary>
/// Performs a binary search on the specified collection.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <returns></returns>
public static int BinarySearch<TItem>(this IList<TItem> list, TItem value)
{
    return BinarySearch(list, value, Comparer<TItem>.Default);
}

/// <summary>
/// Performs a binary search on the specified collection.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value
/// with the list items.</param>
/// <returns></returns>
public static int BinarySearch<TItem>(this IList<TItem> list, TItem value,
    IComparer<TItem> comparer)
{
    return list.BinarySearch(value, comparer.Compare);
}
//
///对指定的集合执行二进制搜索。
/// 
///项目的类型。
///搜索项的类型。
///要搜索的列表。
///要搜索的值。
///用于比较值的比较器
///使用列表项。
/// 
公共静态int二进制搜索(此IList列表,
t搜索值,函数比较器)
{
if(list==null)
{
抛出新的ArgumentNullException(“列表”);
}
if(比较器==null)
{
抛出新的ArgumentNullException(“comparer”);
}
整数下限=0;
int upper=list.Count-1;
while(低0)
{
下=中+1;
}
其他的
{
返回中间;
}
}
返回~较低;
}
/// 
///对指定的集合执行二进制搜索。
/// 
///项目的类型。
///要搜索的列表。
///海洋的价值
/// <summary>
/// Performs a binary search on the specified collection.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <typeparam name="TSearch">The type of the searched item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value
/// with the list items.</param>
/// <returns></returns>
public static int BinarySearch<TItem, TSearch>(this IList<TItem> list,
    TSearch value, Func<TSearch, TItem, int> comparer)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }
    if (comparer == null)
    {
        throw new ArgumentNullException("comparer");
    }

    int lower = 0;
    int upper = list.Count - 1;

    while (lower <= upper)
    {
        int middle = lower + (upper - lower) / 2;
        int comparisonResult = comparer(value, list[middle]);
        if (comparisonResult < 0)
        {
            upper = middle - 1;
        }
        else if (comparisonResult > 0)
        {
            lower = middle + 1;
        }
        else
        {
            return middle;
        }
    }

    return ~lower;
}

/// <summary>
/// Performs a binary search on the specified collection.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <returns></returns>
public static int BinarySearch<TItem>(this IList<TItem> list, TItem value)
{
    return BinarySearch(list, value, Comparer<TItem>.Default);
}

/// <summary>
/// Performs a binary search on the specified collection.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value
/// with the list items.</param>
/// <returns></returns>
public static int BinarySearch<TItem>(this IList<TItem> list, TItem value,
    IComparer<TItem> comparer)
{
    return list.BinarySearch(value, comparer.Compare);
}
var numbers = new List<int>() { ... };
var items = new List<FooInt>() { ... };

int result1 = numbers.BinarySearchIndexOf(5);
int result2 = items.BinarySearchIndexOfBy(foo => foo.bar, 5);
public static class BinarySearchUtils
{
    public static int BinarySearchIndexOf<TItem>(this IList<TItem> list,
        TItem targetValue, IComparer<TItem> comparer = null)
    {
        Func<TItem, TItem, int> compareFunc =
            comparer != null ? comparer.Compare :
            (Func<TItem, TItem, int>) Comparer<TItem>.Default.Compare;
        int index = BinarySearchIndexOfBy(list, compareFunc, targetValue);
        return index;
    }

    public static int BinarySearchIndexOfBy<TItem, TValue>(this IList<TItem> list,
        Func<TItem, TValue, int> comparer, TValue value)
    {
        if (list == null)
            throw new ArgumentNullException("list");

        if (comparer == null)
            throw new ArgumentNullException("comparer");

        if (list.Count == 0)
            return -1;

        // Implementation below copied largely from .NET4
        // ArraySortHelper.InternalBinarySearch()
        int lo = 0;
        int hi = list.Count - 1;
        while (lo <= hi)
        {
            int i = lo + ((hi - lo) >> 1);
            int order = comparer(list[i], value);

            if (order == 0)
                return i;
            if (order < 0)
            {
                lo = i + 1;
            }
            else
            {
                hi = i - 1;
            }
        }

        return ~lo;
    }
}
[TestFixture]
public class BinarySearchUtilsTest
{
    [Test]
    public void BinarySearchReturnValueByMsdnSpecification()
    {
        var numbers = new List<int>() { 1, 3 };

        // Following the MSDN documentation for List<T>.BinarySearch:
        // http://msdn.microsoft.com/en-us/library/w4e7fxsh.aspx

        // The zero-based index of item in the sorted List(Of T), if item is found;
        int index = numbers.BinarySearchIndexOf(1);
        Assert.AreEqual(0, index);

        index = numbers.BinarySearchIndexOf(3);
        Assert.AreEqual(1, index);


        // otherwise, a negative number that is the bitwise complement of the
        // index of the next element that is larger than item
        index = numbers.BinarySearchIndexOf(0);
        Assert.AreEqual(~0, index);

        index = numbers.BinarySearchIndexOf(2);
        Assert.AreEqual(~1, index);


        // or, if there is no larger element, the bitwise complement of Count.
        index = numbers.BinarySearchIndexOf(4);
        Assert.AreEqual(~numbers.Count, index);
    }
}