自定义二分搜索方法未被引用问题排查及功能实现咨询
问题分析与解决方案
咱们一步步拆解你的问题:先说说为什么原代码没法调用自定义的BinarySearch方法,再修复更新后代码里的bug,最后解决你需要的大小写不敏感+部分(前缀)匹配的核心需求。
一、原代码未引用自定义BinarySearch的原因
你的自定义BinarySearch方法参数是string[] directory,但实际项目里的directory是record[]类型的数组——类型完全不兼容,所以编译器根本不允许你调用这个方法,这就是原代码只能用Array.BinarySearch的原因。
而且原代码里的Array.BinarySearch本身也是错误的:它默认会直接比较record对象本身,而不是比较record里的surname字段,所以返回的索引大概率是无效的,甚至会抛出异常。
二、更新后代码的可行性问题
你更新后的代码修正了参数类型(把string[]改成了record[]),现在可以调用自定义方法了,但存在几个关键问题:
- 循环条件错误:用
first < last会导致最后一个元素永远不会被检查到,正确的循环条件应该是first <= last。 - 边界调整逻辑错误:直接设置
last = middle或first = middle会陷入死循环(比如当first和last相差1时,middle等于first,设置first=middle后循环永远不会结束),正确的做法是last = middle - 1和first = middle + 1。 - 不支持部分匹配:当前的
BinarySearch是精确匹配,但你的需求是「输入部分姓氏就能找到所有前缀匹配的记录」,精确匹配的二分搜索完全满足不了这个需求。
三、修复方案(适配部分匹配需求)
根据你的需求,我提供两种方案:一种是修复二分搜索并适配前缀匹配(适合大数据量场景),另一种是用LINQ实现(代码更简洁,适合中小数据量)。
方案1:修复二分搜索,支持前缀匹配
我们需要先找到第一个符合前缀匹配的记录索引,然后从这个位置开始遍历,直到不再匹配为止(因为数组是有序的,后面的记录不会再符合条件)。
修复后的前缀搜索二分方法
private int FindPrefixStartIndex(record[] directory, string searchTerm) { int first = 0; int last = directory.Length - 1; int startIndex = -1; string upperSearchTerm = searchTerm.ToUpper(); int searchLength = upperSearchTerm.Length; // 修正循环条件:first <= last,确保所有元素都被检查 while (first <= last) { // 用first + (last-first)/2避免整数溢出,比直接(first+last)/2更安全 int middle = first + (last - first) / 2; string upperSurname = directory[middle].surname.ToUpper(); // 检查当前姓氏是否以搜索词为前缀(大小写不敏感) bool isPrefixMatch = upperSurname.StartsWith(upperSearchTerm); if (isPrefixMatch) { // 找到匹配项,但继续向左寻找更早的匹配记录 startIndex = middle; last = middle - 1; } else if (string.Compare(upperSurname, upperSearchTerm, StringComparison.OrdinalIgnoreCase) > 0) { // 当前姓氏比搜索词大,向左缩小范围 last = middle - 1; } else { // 当前姓氏比搜索词小,向右缩小范围 first = middle + 1; } } return startIndex; }
更新后的SearchSurname方法
private void SearchSurname() { // 按姓氏大小写不敏感排序,确保二分搜索的正确性 Array.Sort(directory, (x, y) => string.Compare(x.surname, y.surname, StringComparison.OrdinalIgnoreCase)); ClearForm(); string searchTerm = txtSurname.Text.Trim(); if (string.IsNullOrEmpty(searchTerm)) { // 搜索词为空时,可选择显示所有记录或提示用户 return; } int startIndex = FindPrefixStartIndex(directory, searchTerm); if (startIndex == -1) { // 没有找到匹配的记录 return; } // 从起始索引开始遍历,收集所有前缀匹配的记录 string upperSearchTerm = searchTerm.ToUpper(); for (int i = startIndex; i < directory.Length; i++) { string upperSurname = directory[i].surname.ToUpper(); if (upperSurname.StartsWith(upperSearchTerm)) { ListViewItem recordItem = new ListViewItem(directory[i].surname); recordItem.SubItems.Add(directory[i].forename); recordItem.SubItems.Add(directory[i].extCode.ToString()); lvDirectory.Items.Add(recordItem); } else { // 数组是有序的,后面的记录不会再匹配,提前退出循环提升效率 break; } } }
方案2:用LINQ实现(更简洁,适合中小数据量)
如果你的directory数组数据量不大(比如几千条以内),用LINQ会更简单,不需要手动实现复杂的二分搜索,代码可读性更高:
private void SearchSurname() { ClearForm(); string searchTerm = txtSurname.Text.Trim(); if (string.IsNullOrEmpty(searchTerm)) { return; } // 过滤出所有姓氏前缀匹配的记录,大小写不敏感,并按姓氏排序 var matchingRecords = directory .Where(r => r.surname.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) == 0) .OrderBy(r => r.surname, StringComparer.OrdinalIgnoreCase); foreach (var record in matchingRecords) { ListViewItem recordItem = new ListViewItem(record.surname); recordItem.SubItems.Add(record.forename); recordItem.SubItems.Add(record.extCode.ToString()); lvDirectory.Items.Add(recordItem); } }
四、验证可行性
- 方案1的二分搜索修复后,能高效定位到前缀匹配的起始位置,遍历收集所有匹配记录,完全满足大小写不敏感和部分匹配的需求,适合大数据量场景。
- 方案2的LINQ实现代码简洁,逻辑清晰,对于中小数据量来说性能足够,开发效率更高。
内容的提问来源于stack exchange,提问作者LAndrewsEA
相关产品推荐
相关产品推荐

