【问题标题】:Binary search on list列表中的二进制搜索
【发布时间】:2014-11-22 13:35:40
【问题描述】:

我有以下代码,它根据 IP 地址获取国家/地区 ID:

countryID = GetAllCountryIPRanges().Single(c => c.BeginIPNum <= intIp && intIp <= c.EndIPNum).CountryID;

不幸的是,它很慢,因为有大约 200,000 条记录。范围不重叠,GetAllCountryIPRanges()BeginIPNum 升序排列。

如何在此列表中实现.BinarySearch() 以找到正确的记录?

【问题讨论】:

  • 如果您需要快速访问值,也许值得考虑使用Dictionary 而不是一些IEnumerable 集合。
  • 字典不适合,因为我们正在搜索存储范围之间的不精确值(请参阅有效但缓慢的示例)
  • 如果您将范围保留在Tuple 中并使用Tuple 作为键?
  • 实施是什么意思? List.BinarySearch(T item) 有一个默认实现。你可以用对吗?
  • 在这种情况下,我将自己实现二进制搜索。由于二分搜索很容易实现,因此实现比较器以与内置二分搜索一起使用的怪癖似乎不值得。

标签: c# list binary-search


【解决方案1】:

List 有一种二分查找方法,但由于二分查找很容易实现,而且由于范围问题,您需要定义的 IComparator 非常复杂,我建议您实现二分查找方法

类似这样的东西(未经测试!)

public static IPRange BinarySearch(List<IPRange> source, int intIp)
{
    int startIndex = 0;
    int endIndex = source.Count;
    while (endIndex >= startIndex)
    {
        int middleIndex = startIndex + (endIndex - startIndex) / 2;
        if (source[middleIndex].BeginIPNum <= intIp && intIp <= source[middleIndex].EndIPNum)
        {
            return source[middleIndex];
        }
        else if (source[middleIndex].BeginIPNum < intIp)
        {
            startIndex = middleIndex + 1;
        }
        else
        {
            endIndex = middleIndex - 1;
        }
    }

    return null;
}

假设列表已排序并且没有重叠范围。

【讨论】:

  • “二分搜索很容易实现” - 等等:P?
  • 为什么要定义一个比较器复合体?
  • 因为他正在搜索的实际上是一个范围内的单个数字。这意味着他必须将相等定义为其中一个范围在另一个范围内,而这只是相等。他需要将小于定义为一个的结束索引大于另一个的开始索引,反之则为大于。所有这些都需要驻留在内部类中并实现接口。然后他需要创建一个 Range 对象,其中两个限制都是搜索到的数字,然后他需要使用该范围和比较器对象调用内置 BS。实施 BS 似乎更容易。
【解决方案2】:

一定喜欢递归的优雅...(未测试)

public static IPRange BinarySearch(IList<IPRange> ipList, int ip)
{
   return BinarySearch(source, ip, 0, ipList.Count - 1);
}

public static IPRange BinarySearch(IList<IPRange> ipList, int ip, int min, int max)
{      
   if (min > max)
   {
     throw new Assertion("Error: ipList is empty, out-of-order, or does not contain an element that includes the IP");
   } 
   int mid = (min + max) / 2;
   var midIp = ipList[mid];
   if (ip < midIp.BeginIpNum)
   {
     return BinarySearch(ipList, ip, min, mid-1);
   }
   if (ip > midIp.EndIpNum)
   {
     return BinarySearch(ipList, ip, mid+1, max);
   }
   return midIp; 
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-19
  • 2017-07-06
  • 2013-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
相关资源
最近更新 更多