【问题标题】:Binary Search a List of custom data types to match just one field二进制搜索自定义数据类型列表以仅匹配一个字段
【发布时间】:2011-09-23 05:03:53
【问题描述】:

我有一个列表:

List<Student> allStudents = new List<Student>(); 

包含超过 94,000 个 Student 对象,其中 Student 定义为:

public class Student
{
    public Int32 ID { get; set; }
    public String Surname { get; set; }
    public String Other_Names { get; set; }
    public String DOB { get; set; }
    // remaining fields omitted
}

并按姓氏排序。

从另一个来源获取 Student 对象后,我想对 List allStudents 进行二进制搜索,以仅基于 Surname 属性查找匹配项。例如,如果 List allStudents 中的现有记录是:

Student(8139241, "Flintstone", "Fred", "12/1/1967")

然后我搜索该项目:

Student(7294311, "Flintstone", "Wilma", "14/6/1969")

二分查找应该是成功的。

List.BinarySearch(T, IComparer) 重载似乎是一种可能性,但它是一个可行的解决方案吗?还是有更好的策略?我将处理大量记录和搜索,因此 O(n) 搜索功能将不可行。

提前致谢!

更新:我决定用 Wintellect PowerCollections 库中的 MultiDictionary 替换我的列表。这个 MultiDictionary 可以接受重复的键。

【问题讨论】:

    标签: c# list search


    【解决方案1】:

    List.BinarySearch 是一个很好的解决方案,可以按照您的预期工作。这是一个链接,其中显示了您需要 IComparer 的 solution similar。不过,他们的示例没有使用 Generic IComparer。

    public class CompareCustomDataType : IComparer<Student> {
    
      public int Compare(Student x, Student y)
      {
        if (x == y)    return 0;
        if (x == null) return -1;
        if (y == null) return 1;
    
        return String.Compare(x.Surname, y.Surname);
      }
    ...
    }
    

    【讨论】:

      【解决方案2】:

      有以下限制

      1. 如果 List 包含多个具有相同值的元素,则该方法仅返回其中一个,并且它可能返回任何一个,不一定是第一个。
      2. List 必须已经根据比较器实现进行了排序;否则结果不正确。

      我建议您使用 Linq 从您的列表中查找匹配数据。

        var data = students.where(o => o.SurName='xxxxx');
      

      >您还可以使用谓词从 List 对象中使用 Find 或 FindAll 方法。

      【讨论】:

      • 感谢大家的意见。 @Amit 使用 Linq 有优势吗?我知道 Find 和 FindAll 是线性搜索; Linq 查询是否更好地摊销?
      • 我不是 linq 专家;Linq 使用线性搜索。 Linq 是一种结构化的编程技术,它使代码更具可读性和可理解性。就像如果您看到语句很明显,代码行正在尝试查找列表中与姓氏字段匹配的所有项目。
      【解决方案3】:

      有了这么多条目,您可能最好使用Dictionary&lt;string, Student&gt; 查找,这将摊销 O(1)。虽然可能有多个同姓的学生,所以应该是Dictionary&lt;string, List&lt;Student&gt;&gt;

      正如 Amit 所指出的,当存在重复项时使用二分搜索可能会很棘手,因为您不知道将在一系列重复项中获得哪个索引。您必须在返回的索引的左侧和右侧搜索以查看是否存在任何其他匹配的姓氏。

      【讨论】:

      • 感谢您的建议。由于 O(1) 摊销,Dictionary 的想法似乎非常有吸引力,但我曾经使用过的最接近的东西是 Perl 哈希,所以我正在努力解决它。确切地说,我需要在我的 List 和数据表毕业生(姓氏,其他名称,...,...,)之间找到匹配项。仅有的两个共同字段是 Surname 和 Other_Names,因此显然它们是唯一要匹配的字段。
      • 但是由于字典每对只能有一个键,并且我需要匹配两个字符串(姓氏和其他名称),我需要考虑如何构造字典以便它可以匹配列表。呃,细节……
      • 也许我可以定义一个类名称(姓氏,其他名称),然后制作一个字典()。但是,如果有几个同名的人(例如来自加利福尼亚的约翰史密斯和来自爱达荷州的约翰史密斯),则密钥将不再是唯一的,这很糟糕。然后也许将 Name 类定义为 Name(Surname, Other_Names, UniqueID) 会起作用。嗯…………
      • 或者我应该完全摆脱 List 并使用更易于访问的数据结构,例如 Dictionary。因为只要我的所有学生都在 List 中,我仍然需要遍历该 List 以合并我从另一个来源获取的 Student,而不管任何其他优化。现在我需要考虑如何用字典替换我的列表。
      【解决方案4】:

      为您的Student 类定义IComparable&lt;T&gt; 接口。那么你列表的所有排序和比较方法,包括BinarySearch()都会自动使用这个。

      【讨论】:

        猜你喜欢
        • 2013-02-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-04
        • 2012-05-24
        相关资源
        最近更新 更多