如何在一个结构数组上进行二进制搜索w.r.t c#中的特定字段值?

时间:2012-01-09 14:43:27

标签: c# arrays struct binary-search

我有一个结构数组,其中struct有三个整数字段。它按照其中一个字段排序,比方说F,我希望有一种方法可以对这个字段进行二进制搜索,也就是二元搜索(mystruct [] myarray,int val)的函数返回索引其中F = val的结构。我知道存在一个现有的Array.BinarySearch(T []数组,T值)函数,但它只允许搜索与数组中的类型相同的类型T.这意味着如果我想要搜索一个值,我需要创建一个新的结构并将字段F设置为该值,以便我可以将它传递给该函数。我不认为会有显着的性能开销,但它看起来很难看。我能想到的另一种方式是自己实现这个功能,但是当存在类似的东西时,这似乎也是不优雅的。对于更好的方式或哪种方式的任何建议都是首选?

3 个答案:

答案 0 :(得分:4)

你可以为你的结构实现IComparable<T>以在字段(F)上进行比较,或者你可以为你的结构创建一个IComparer<>,它将根据该字段进行比较并将其传递给{{ 1}}。

所以:

Array.BinarySearch()

可以称为:

// using IComparable<T>
public struct MyStruct : IComparable<MyStruct>
{
    public int F { get; set; }

    // other fields that should not affect "search"
    public int X { get; set; }

    public int CompareTo(MyStruct other)
    {
        return F.CompareTo(other.F);
    }
}

或单独的MyStruct target = new MyStruct { F = 13 }; Array.BinarySearch(arrayOfMyStruct, target);

IComparer<MyStruct>

可以这样称呼:

public struct MyStruct 
{
    public int F { get; set; }

    // other non-sort/search affecting properties
    public int X { get; set; }
}

public struct MyStructComparer : IComparer<MyStruct>
{
    public int Compare(MyStruct x, MyStruct y)
    {
        return x.F.CompareTo(y.F);
    }
}

第一个代码较少,但它强烈地将排序与类型相结合,如果你想根据情况改变排序(即允许多个排序顺序),这并不理想。后者提供了更大的灵活性,因为您可以提供独立于结构的多个不同的顺序,但它确实需要额外的类。

<强>更新

如果您不想创建虚拟结构进行比较,可以实现MyStruct target { F = 13; } Array.BinarySearch(myArrayOfStruct, target, new MyStructComparer()); ,如:

IComparable

可以这样称呼:

public struct MyStruct : IComparable
{
    public int F { get; set; }

    // other non-sort/search affecting properties
    public int X { get; set; }

    public int CompareTo(object other)
    {
        // if the type is NOT an int, you can decide whether you'd prefer
        // to throw, but the concept of comparing the struct to something
        // unknown shouldn't return a value, should probably throw.
        return F.CompareTo((int)other);
    }
}

但是,这又强烈地将你的类的实现绑定到给定的比较类型,我认为这比使用虚拟搜索目标更加丑陋,但这是我个人的偏好。就个人而言,特别是初始化语法尽可能短,我更喜欢使用虚拟目标:

Array.BinarySearch(arrayOfMyStruct, 13);

更新:您可以同时支持var target = new MyStruct { F = 13 }; int比较,但它会很快变得混乱,这就是为什么我个人再次建议使用虚拟结构来避免头痛:

MyStruct

答案 1 :(得分:1)

一种方法是创建一个自定义IComparer<T>,仅根据该字段的值比较结构的实例,并将其传递给BinarySearch的{​​{3}}(您还需要创建一个“虚拟”结构实例来比较)。这可能是最纯粹的解决方案。

但是,作为一个实际问题,您可以使用LINQ投影到字段值的集合和二进制搜索;结果索引将与您搜索结构集合本身相同。例如:

var structs = new MyStruct[n];
var index = structs.Select(i => i.F).ToList().BinarySearch(42);

在上面的代码中,F是字段的vame,42是您要搜索的值(其类型将是F的类型)。这不会那么快,但你不需要编写任何代码,速度在你的情况下很可能无关紧要。

更新:为了澄清:显然,由于投影操作,上面的代码将是O(n)所以在投影之后使用二进制搜索一次是愚蠢的(你可以简单地进行线性搜索代替)。但是,如果您打算进行多次搜索,那么它可能会开始有意义。

我绝对不建议在您的结构中覆盖Equals,除非将实例之间的比较缩减为比较应用程序中<{strong>> 的F成员

答案 2 :(得分:1)

如果你的struct实现了IComparable,你可以使用:

// myValue is an the value of the field to compare to
Array.BinarySearch(myArray, myValue);

http://msdn.microsoft.com/en-us/library/y15ef976.aspx

中所述

您可以使用IComparable将结构与对象进行比较,因此您可以传入新结构的值intead。在您的CompareTo实现中,您可以将任何值与字段值进行比较,从而允许您说“我的结构应该被视为更大/小于此数字”。

编辑:

以下是您的struct的CompareTo示例:

public int CompareTo(object obj)
{
    if (obj is int)
    {
        return myIntField.CompareTo((int)obj);
    }
    return 0;
}