C# 如何使用C搜索范围内的值#

C# 如何使用C搜索范围内的值#,c#,algorithm,C#,Algorithm,我有一个这样的值列表 1000, 20400 22200, 24444 范围不重叠 我想做的是有一个c#函数,它可以存储(从db加载的值,然后在本地缓存)这些值的相对较大的列表,然后有一个方法来查找提供的值是否在任何范围内 这有意义吗 需要最快的解决方案您已经指定了值,但随后谈到了范围 对于仅值,我将使用哈希集。对于范围,它变得更复杂。。。让我们知道这是否是你真正想要的,我会仔细考虑的。如果它们是范围,您有关于它们的额外信息吗?你知道它们是否会重叠吗?您只是对某个范围的存在感兴趣,还是需要找到

我有一个这样的值列表

1000, 20400
22200, 24444
范围不重叠

我想做的是有一个c#函数,它可以存储(从db加载的值,然后在本地缓存)这些值的相对较大的列表,然后有一个方法来查找提供的值是否在任何范围内

这有意义吗


需要最快的解决方案

您已经指定了值,但随后谈到了范围

对于仅值,我将使用
哈希集
。对于范围,它变得更复杂。。。让我们知道这是否是你真正想要的,我会仔细考虑的。如果它们是范围,您有关于它们的额外信息吗?你知道它们是否会重叠吗?您只是对某个范围的存在感兴趣,还是需要找到某个值所属的所有范围

编辑:通过对问题的编辑,巴里的答案完全正确。只需在初始化时进行排序(按下限排序就足够了),然后进行二进制搜索,以找到包含该值的范围,或者不包含该值

编辑:我最近在中找到了下面的代码

需要事先对范围进行排序-
列表。如果没有重叠,则排序可以正常工作

public class Range : IComparable<Range>
{
      private readonly int bottom; // Add properties for these if you want
      private readonly int top;

      public Range(int bottom, int top)
      {
             this.bottom = bottom;
             this.top = top;
      }

      public int CompareTo(Range other)
      {
             if (bottom < other.bottom && top < other.top)
             {
                   return -1;
             }
             if (bottom > other.bottom && top > other.top)
             {
                   return 1;
             }
             if (bottom == other.bottom && top == other.top)
             {
                   return 0;
             }
             throw new ArgumentException("Incomparable values (overlapping)");
      }

      /// <summary>
      /// Returns 0 if value is in the specified range;
      /// less than 0 if value is above the range;
      /// greater than 0 if value is below the range.
      /// </summary>
      public int CompareTo(int value)
      {
             if (value < bottom)
             {
                   return 1;
             }
             if (value > top)
             {
                   return -1;
             }
             return 0;
      }
}

// Just an existence search
public static bool BinarySearch(IList<Range> ranges, int value)
{
    int min = 0;
    int max = ranges.Count-1;

    while (min <= max)
    {
        int mid = (min + max) / 2;
        int comparison = ranges[mid].CompareTo(value);
        if (comparison == 0)
        {
            return true;
        }
        if (comparison < 0)
        {
            min = mid+1;
        }
        else if (comparison > 0)
        {
            max = mid-1;
        }
    }
    return false;
}
公共类范围:i可比较
{
private readonly int bottom;//如果需要,可以添加这些属性
私有只读int-top;
公共范围(整数底部、整数顶部)
{
this.bottom=底部;
this.top=top;
}
公共整数比较(范围其他)
{
if(底部<其他.bottom&&top<其他.top)
{
返回-1;
}
如果(底部>其他.bottom&&top>其他.top)
{
返回1;
}
if(bottom==other.bottom&&top==other.top)
{
返回0;
}
抛出新的ArgumentException(“不可比较的值(重叠)”;
}
/// 
///如果值在指定范围内,则返回0;
///如果值高于范围,则小于0;
///如果值低于范围,则大于0。
/// 
公共整数比较(整数值)
{
如果(值<底部)
{
返回1;
}
如果(值>顶部)
{
返回-1;
}
返回0;
}
}
//只是一个生存搜索
公共静态布尔二进制搜索(IList范围,int值)
{
int min=0;
int max=范围。计数-1;
而(最小0)
{
max=mid-1;
}
}
返回false;
}

二进制搜索就可以了。保持区域列表的排序顺序,确保所有区域都不相交(如果相交,则合并)。然后编写一个二进制搜索,它不是针对单个值进行测试,而是针对范围的任意一端进行测试,以选择上面或下面的选项。

我会先尝试最简单的选项,如果不满足您的需要,则进行优化

class Range {
   int Lower { get; set; }
   int Upper { get; set; }
}

List<Range>.FirstOrDefault(r => i >= r.Lower && i <= r.Upper);
类范围{
int Lower{get;set;}
整数上限{get;set;}
}

List.FirstOrDefault(r=>i>=r.Lower&&i假设范围不重叠:

->将您的所有范围编号放入一个数组中

->对数组进行排序

->同时为startValue保留一个哈希集

->现在对您的号码进行二进制搜索。有两种可能:

-->您的数字左边(小于)的数组范围是一个起始值:您的 数字在范围内

-->您的数字左边(小于)的数组范围不是起始值:您的 数字不在范围内


这在功能上是您所追求的吗?如果是这样,您只是希望它更高效,而不是将ValueRangeCollection中的foreach更改为二进制搜索

    public struct ValueRange 
    { 
       public int LowVal; 
       public int HiVal; 
       public bool Contains (int CandidateValue) 
       { return CandidateValue >= LowVal && CandidateValue <= HiVal; } 
       public ValueRange(int loVal, int hiVal)
       {
          LowVal = loVal;
          HiVal = hiVal;
       }
   }

    public class ValueRangeCollection: SortedList<int, ValueRange> 
    { 
        public bool Contains(int candValue) 
        {  
            foreach ( ValueRange valRng in Values)
                if (valRng.Contains(candValue)) return true;
            return false; 
        }
        public void Add(int loValue, int hiValue)
        {
            Add(loValue, new ValueRange(loValue, hiValue));
        }
    }
public结构值范围
{ 
公共国际组织;
公共国际组织;
公共布尔包含(int CandidateValue)
{return CandidateValue>=LowVal&&CandidateValue
类范围
{
public int Start{get;set;}
公共int End{get;set;}
静态字典值;
静态int[]arrtobinarysarchin;
公共静态void BuildRanges(IEnumerable ranges){
值=新字典();
foreach(范围内的var项目)
值[项目.开始]=项目;
arrtobinarysarcin=values.Keys.ToArray();
Array.Sort(arrtobinarysarcin);
}
公共静态范围GetRange(int值)
{
int searchIndex=Array.BinarySearch(arrtobinarysarchin,value);
如果(搜索索引<0)
searchIndex=~searchIndex-1;
如果(搜索索引<0)
返回null;
建议的范围范围=值[arrtobinarysarchin[searchIndex]];
如果(proposedRange.End>=值)
返回建议的范围;
返回null;
}
}
类别范围
{
int[]start=new[]{100022200};
int[]end=new[]{20400,24444};
公共整数范围索引(整数测试)
{
int指数=-1;

如前所述,如果(test>=开始[0]&&test,如果范围集很大且不重叠,最好进行二进制搜索。一种方法是使用
SortedDictionary
,它实现了一个红黑树来给出一个O(log(n))搜索时间。我们可以使用范围作为键,并通过将要匹配的单个值转换为单个点的范围来执行字典查找。如果我们实现
CompareTo
方法,以便重叠的范围被视为相等/匹配,字典查找将查找匹配范围以供使用

public struct Range : IComparable<Range>
{
    public int From;
    public int To;

    public Range(int point)
    {
        From = point;
        To = point;
    }

    public Range(int from, int to)
    {
        From = from;
        To = to;
    }

    public int CompareTo(Range other)
    {
        // If the ranges are overlapping, they are considered equal/matching
        if (From <= other.To && To >= other.From)
        {
            return 0;
        }

        // Since the ranges are not overlapping, we can compare either end
        return From.CompareTo(other.From);
    }
}

public class RangeDictionary
{
    private static SortedDictionary<Range, string> _ranges = new SortedDictionary<Range, string>();

    public RangeDictionary()
    {
        _ranges.Add(new Range(1, 1000), "Alice");
        _ranges.Add(new Range(1001, 2000), "Bob");
        _ranges.Add(new Range(2001, 3000), "Carol");
    }

    public string Lookup(int key)
    {
        /* We convert the value we want to lookup into a range,
         * so it can be compared with the other ranges */
        var keyAsRange = new Range(key);
        string value;
        if (_ranges.TryGetValue(keyAsRange, out value))
        {
            return value;
        }
        return null;
    }
}
在这种情况下,
值将包含字符串
“Bob”
,因为1356与范围1001-2000匹配

在您的例子中,如果您对获取范围本身感兴趣,您可以在字典中使用该范围作为键和值。示例代码可以很容易地扩展为保存泛型值

作为sid
class Ranges
{
    int[] starts = new[] { 1000, 22200 };
    int[] ends = new[] { 20400, 24444 };

    public int RangeIndex(int test)
    {
        int index = -1;

        if (test >= starts[0] && test <= ends[ends.Length - 1])
        {
            index = Array.BinarySearch(ends, test);

            if (index <= 0)
            {
                index = ~index;
                if (starts[index] > test) index = -1;
            }
        }

        return index;
    }
}
public struct Range : IComparable<Range>
{
    public int From;
    public int To;

    public Range(int point)
    {
        From = point;
        To = point;
    }

    public Range(int from, int to)
    {
        From = from;
        To = to;
    }

    public int CompareTo(Range other)
    {
        // If the ranges are overlapping, they are considered equal/matching
        if (From <= other.To && To >= other.From)
        {
            return 0;
        }

        // Since the ranges are not overlapping, we can compare either end
        return From.CompareTo(other.From);
    }
}

public class RangeDictionary
{
    private static SortedDictionary<Range, string> _ranges = new SortedDictionary<Range, string>();

    public RangeDictionary()
    {
        _ranges.Add(new Range(1, 1000), "Alice");
        _ranges.Add(new Range(1001, 2000), "Bob");
        _ranges.Add(new Range(2001, 3000), "Carol");
    }

    public string Lookup(int key)
    {
        /* We convert the value we want to lookup into a range,
         * so it can be compared with the other ranges */
        var keyAsRange = new Range(key);
        string value;
        if (_ranges.TryGetValue(keyAsRange, out value))
        {
            return value;
        }
        return null;
    }
}
var ranges = new RangeDictionary();
var value = ranges.Lookup(1356);