【问题标题】:List<T> Binary search returns wrong valueList<T> 二分查找返回错误值
【发布时间】:2014-09-10 15:07:53
【问题描述】:

即使列表已排序,二分查找也会返回错误值。这是列表:

1707 ABCD
1707 XXXX
1725 DEFG
1725 HIJK
1725 LMNOP

我从按时间(第一列)预排序的文件中获取此列表,因此我不在我的代码中对其进行排序。当我对 1725 DEFG 进行二进制搜索时,它会在按位补码之前返回 1725 LMNOP。如果我进行按位补码,结果将是 1725 HIJK。

为什么会这样?

下面是实现:

 public class RecComparer: IComparer<MyData>
{
 public  int Compare(MyData x, MyData y)
{
    if (x.DateDetails == null)
    {
        if (y.DateDetails == null)
        {
                           return 0;
        }
        else
        {

            return -1;
        }
    }
    else
    {

        if (y.DateDetails == null)
                      {
            return 1;
        }
        else
        {
            int retval = x.DateDetails.Length.CompareTo(y.DateDetails.Length);

            if (retval != 0)
            {

                return retval;
            }
            else
            {

                return x.DateDetails.CompareTo(y.DateDetails);
            }
        }
    }
}
}

这是对 BinarySearch 的调用:

lookAhead = recordList.BinarySearch(lookAheadtime, (IComparer<MyData>)rc);

任何原因都是它的行为方式。

编辑:

public class MyData
{
    public string DateDetails { get; set; }
    public string TrackDetails { get; set; }
}

【问题讨论】:

  • 我不相信二进制搜索适用于非唯一键
  • 那么最好的实现方式是什么。
  • @DarrenKopp:如果是这种情况,我是否应该也包括列表的第二列,而不是仅按第一列搜索。
  • 您必须重现列表的排序方式。所以比较字符串长度是完全不合适的。您必须首先从字符串中挖掘出时间,就像原始代码一样,首先进行比较。然后仅在时间相同时才比较其余部分。 not 使用字符串但首先将源代码解析到您自己的类中,您会领先一步。
  • 你为什么要先用长度来比较?

标签: c# list binary-search


【解决方案1】:

更新 #2:在包含重复项的列表中使用自定义比较器进行二分搜索。

假设在“1707 ABCD”中,“1707”是DateDetails,而“ABCD”是TrackDetails,那么您有一个包含两个字段的数据数组,这些字段已经按其中一个字段的顺序排序两个字段,即DateDetails。因此可能有多个条目具有相同的DateDetails

如果您执行 BinarySearch,它将返回具有指定 DateDetails 的条目之一,不一定是第一个。然后您必须向后扫描才能找到第一个。

public static class ListHelper
{
    /// <summary>
    /// Return the zero-based index of the first matching item in the sorted List, 
    /// if a match for item is found; otherwise, a negative number that is the bitwise 
    /// complement of the index of the next element that is larger than item or, if there is
    /// no larger element, the bitwise complement of Count.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="item"></param>
    /// <param name="comparer"></param>
    /// <returns>The zero-based index of the first matching item in the sorted List, 
    /// if item is found; otherwise, a negative number that is the bitwise complement 
    /// of the index of the next element that is larger than item or, if there is
    /// no larger element, the bitwise complement of Count.</returns>
    public static int BinarySearchFirst<T>(this List<T> list, T item, IComparer<T> comparer)
    {
        int start = list.BinarySearch(item, comparer);
        for (; start > 0 && comparer.Compare(list[start], list[start - 1]) == 0; start--)
            ;
        return start;
    }
    /// <summary>
    /// Find the zero-based indices of the first and last matching items in the sorted List, 
    /// if a match for item is found; otherwise, a negative number for both that is the bitwise 
    /// complement of the index of the next element that is larger than item or, if there is
    /// no larger element, the bitwise complement of Count.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="item"></param>
    /// <param name="comparer"></param>
    /// <param name="start">The zero-based index of the first matching item in the List, 
    /// if item is found; otherwise, a negative number that is the bitwise complement 
    /// of the index of the next element that is larger than item or, if there is
    /// no larger element, the bitwise complement of Count.</param>
    /// <param name="end">The zero-based index of the first matching item in the sorted List, 
    /// if item is found; otherwise, a negative number that is the bitwise complement 
    /// of the index of the next element that is larger than item or, if there is
    /// no larger element, the bitwise complement of Count.</param>
    /// <returns>true if found, else false</returns>
    public static bool BinarySearchRange<T>(this List<T> list, T item, IComparer<T> comparer, out int start, out int end)
    {
        start = end = list.BinarySearch(item, comparer);
        if (start < 0)
            return false;
        for (; start > 0 && comparer.Compare(list[start], list[start - 1]) == 0; start--)
            ;
        for (int last = list.Count - 1; end < last && comparer.Compare(list[end], list[end + 1]) == 0; end++)
            ;
        return true;
    }
}

【讨论】:

  • 这对我有用。我必须进行一项更改才能使其正常工作: for (; start > 0 && rc.Compare(recordList[~lookAhead], recordList[start - 1]) == 0; start--) ;
【解决方案2】:

我不确定我是否理解这个问题,但你不能只使用 linq 搜索列表吗?

recordList.Where(x=>x.columnName == 1725).First();

【讨论】:

  • 不,事实并非如此,这只是上面我遇到问题的一个例子。如果我将您的代码与 LINQ 一起使用,我将无法搜索最接近的值
猜你喜欢
  • 2016-05-14
  • 2010-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 2017-01-29
相关资源
最近更新 更多