在排序范围数据中查找整数的最快方法

时间:2014-09-10 21:06:03

标签: .net

我有一些排序数据,它们具有每个范围的起始和结束整数,并且它们是连续的。所以我的数据可能如下所示:

Number Start End 
0      0     47 
1      48    94 
2      95    287 
3      288   1123

等等。

我会得到一个像113这样的整数,我希望以最快的方式搜索数据以找到匹配的数字。我可以将数据粘贴到一些优化检索/比较速度的结构中。

我的数据非常大。

编辑: 我选择了一个答案,这是我最终得到的代码:

  Public Function EndingCaptureNumber(CaptureEnd As Integer) As Integer
    EndingCaptureNumber = CaptureEnds.BinarySearch(CaptureEnd)
    If EndingCaptureNumber < 0 Then
      Return (Not EndingCaptureNumber) - 1
    End If
  End Function

捕获结束是每个范围结束的列表。不是恭维。由于这会发现第一个更大,我减去1得到最后一个不大。

编辑:重复问题重新审核

由此产生的答案使用内置的BinarySearch,但处理不完全匹配的值。寻求其他文章的搜索者不会学习这个(imho)更好的答案。另外,另一个问题是OP在他的RL问题中使用的实际数据类型混乱。

3 个答案:

答案 0 :(得分:2)

由于您只需要下限,您可以将其存储在SortedList<int, int>中,其中键是下限,值是数字,如下所示:

static SortedList<int, int> Ranges = new SortedList<int, int>
{
    {0, 0}, {48, 1}, {95, 2}, {288, 3} 
};

现在您可以使用此扩展方法查找下一个更高数字的索引:

private static int BinarySearch<T>(IList<T> list, T value)
{
    if (list == null)
        throw new ArgumentNullException("list");
    var comp = Comparer<T>.Default;
    int lo = 0, hi = list.Count - 1;
    while (lo < hi)
    {
        int m = (hi + lo) / 2;  // this might overflow; be careful.
        if (comp.Compare(list[m], value) < 0) lo = m + 1;
        else hi = m - 1;
    }
    if (comp.Compare(list[lo], value) < 0) lo++;
    return lo;
}

public static int FindFirstIndexGreaterThanOrEqualTo<T, U>(this SortedList<T, U> sortedList, T key)
{
    return BinarySearch(sortedList.Keys, key);
}

(对此答案的肯定:https://stackoverflow.com/a/594528/284240

...并按照以下代码获取数字:

int number;
int find = 113;
int pos = Ranges.FindFirstIndexGreaterThanOrEqualTo(find);
if (pos > 0)
{
    int key = Ranges[pos];
    if(key == find) 
        number = Ranges[Ranges.Keys[pos]]; // matches lowerbound
    else
        number = Ranges[Ranges.Keys[pos - 1]]; // in range        
}

答案 1 :(得分:0)

我认为这是最快捷的方式。但是,它必须设置并使用大量内存。

   int[] Numbers = new int[100000];

   for (int i = 0; i <= 47; i++)
       Numbers[i] = 0;
   for (int i = 48; i <= 94; i++)
       Numbers[i] = 1;
   for (int i = 95; i <= 287; i++)
       Numbers[i] = 2;
   for (int i = 288; i <= 1123; i++)
       Numbers[i] = 3;

   int Result = Numbers[113];   // Fast!

答案 2 :(得分:0)

坚持二元搜索。如果您有N个范围并查找K个数字,则搜索将采用O(KlogN)。

使用@Steve Wellens建议传播一切将需要大量的设置 - O(R)(R是最后一个范围结束 - 在你的例子中是1123)。设置完成后,K搜索将采用O(K),因此您正在查看O(K + R)

现在,如果最大数量小于KlogN,并且内存不是问题,则展开范围。如果不是(这是我的猜测,你说你有很多数据),二进制搜索会更快。