【问题标题】:Looking for something like a HashSet, but with a range of values for the key?正在寻找类似 HashSet 的东西,但键的值范围?
【发布时间】:2016-08-22 10:27:39
【问题描述】:

我想知道是否有类似 HashSet 的东西,但由一系列值键控。

例如,我们可以添加一个以 100 到 4000 之间的所有整数为键的项目。如果我们使用 100 到 4000 之间的任何键,则该项目将被返回,例如287.

我希望查找速度非常接近 HashSet,即 O(1)。可以使用二进制搜索来实现这一点,但这对于要求来说太慢了。我想尽可能使用标准的 .NET API 调用。

更新

这很有趣:https://github.com/mbuchetics/RangeTree

它的时间复杂度为 O(log(N)),其中 N 是间隔数,所以它不完全是 O(1),但它可以用来构建一个有效的实现。

【问题讨论】:

  • 为什么不直接将 HashSet 与匹配所需逻辑的比较器一起使用?
  • 但是 HashSet 没有键。
  • 对范围有什么了解吗?或者它们可以是什么?
  • 如果我理解正确,您需要那么多键(整数范围)链接到同一个项目吗?如果这是正确的,我可能有一个解决方案,但我不确定我是否理解正确。

标签: c# .net


【解决方案1】:

我不相信它已经有一个结构。你可以实现类似 RangedDictionary 的东西:

class RangedDictionary {

   private Dictionary<Range, int> _set = new Dictionary<Range, int>();

   public void Add(Range r, int key) {
      _set.Add(r, key);
   }

   public int Get(int key) {
      //find a range that includes that key and return _set[range]
   }
} 

struct Range {  
   public int Begin;
   public int End;
   //override GetHashCode() and Equals() methods so that you can index a Dictionary by Range
}

编辑:改为 HashSet 到 Dictionary

【讨论】:

  • 我在 msdn 文档中找不到对 HashSet&lt;T,U&gt; 的任何引用。我是否遗漏了某些内容,或者这是来自第三方库还是您实际上是想在这里使用字典?
  • 感谢您的回答。但是,这似乎要求您在要检索项目时知道范围 - 但此时我们不知道范围!
  • 另外值得注意的是,“查找包含该键的范围并返回 _set[range]”的过程不会是 O(1),因为我认为 OP 要求.. .
  • @Chris:是的,我的意思是字典。好吧,我们在检索时不需要知道范围。我们只需要想出一种基于键检索范围(范围?)的方法。是的 - 这可能超过 O(1),但我在这里看不到任何更好的解决方案。 (除非您有 1-100、101-200 等范围的预定义模式。)
  • 发现有一个专门的数据结构(尽管可能没有在标准 .NET apis 中实现)可以做到这一点。它是一个分段树。它找到对数复杂度值的范围(好吧,就像 bin-search 在这里所做的那样)。但是,范围可以在段树中重叠。 OP对范围不能重叠的限制可以帮助我们降低复杂性吗?不确定。
【解决方案2】:

这是一个您可以尝试的解决方案。但是它假设了一些要点:

  • 没有范围重叠
  • 当您请求一个数字时,它实际上在一个范围内(无错误检查)

根据你的说法,这个是 O(N),但我认为你可以毫不费力地使其成为 O(log(N))。

这个想法是一个类将处理范围的事情,它基本上会将给它的任何值转换为其范围的下边界。这样,您的 Hashtable(此处为字典)包含低边界作为键。

public class Range
{
    //We store all the ranges we have
    private static List<int> ranges = new List<int>();
    public int value { get; set; }

    public static void CreateRange(int RangeStart, int RangeStop)
    {
        ranges.Add(RangeStart);
        ranges.Sort();
    }

    public Range(int value)
    {
        int previous = ranges[0];
        //Here we will find the range and give it the low boundary
        //This is a very simple foreach loop but you can make it better
        foreach (int item in ranges)
        {
            if (item > value)
            {
                break;
            }
            previous = item;
        }
        this.value = previous;
    }

    public override int GetHashCode()
    {
        return value;
    }
}

这是测试它。

class Program
{
    static void Main(string[] args)
    {
        Dictionary<int, int> myRangedDic = new Dictionary<int,int>();
        Range.CreateRange(10, 20);
        Range.CreateRange(50, 100);

        myRangedDic.Add(new Range(15).value, 1000);
        myRangedDic.Add(new Range(75).value, 5000);

        Console.WriteLine("searching for 16 : {0}", myRangedDic[new Range(16).value].ToString());
        Console.WriteLine("searching for 64 : {0}", myRangedDic[new Range(64).value].ToString());

        Console.ReadLine();
    }
}

我不相信您真的可以低于 O(Log(N)),因为您无法立即知道数字在哪个范围内,您必须始终将其与下限(或上限)进行比较.

如果您有预先确定的范围,那会更容易做到。也就是说,如果你的范围是每百个,那么通过计算任何数字的模 100 很容易找到正确的范围,但在这里我们不能假设任何内容,所以我们必须检查。

要使用此解决方案下降到 Log(N),只需将 foreach 替换为将查看数组中间的循环,然后每次迭代将其拆分为两个...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-24
    • 2010-12-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2012-09-21
    相关资源
    最近更新 更多