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