C# 数据结构,C:~O(1)使用范围键查找?
我有一个数据集。此数据集将用作查找表。给定一个数字,我应该能够为该数字查找相应的值 数据集(比如说它的CSV)有一些警告。而不是:C# 数据结构,C:~O(1)使用范围键查找?,c#,data-structures,hash,C#,Data Structures,Hash,我有一个数据集。此数据集将用作查找表。给定一个数字,我应该能够为该数字查找相应的值 数据集(比如说它的CSV)有一些警告。而不是: 1,ABC 2,XYZ 3,LMN 数字为范围(-为“至”,而非负): 所有数字都是有符号整数。没有范围与其他范围重叠。存在一些差距;数据集中有一些未定义的范围(如上面最后一段中的9和10)。 ` 如何在C#中对该数据集建模,以便在保持较低内存占用率的同时获得最高效的查找 我提出的唯一选择是过度消耗内存。假设我的数据集是: 1-2,ABC 4-6,XYZ 然后我
1,ABC
2,XYZ
3,LMN
数字为范围(-为“至”,而非负):
所有数字都是有符号整数。没有范围与其他范围重叠。存在一些差距;数据集中有一些未定义的范围(如上面最后一段中的9和10)。
`
如何在C#中对该数据集建模,以便在保持较低内存占用率的同时获得最高效的查找
我提出的唯一选择是过度消耗内存。假设我的数据集是:
1-2,ABC
4-6,XYZ
然后我创建一个字典()
,其键/值为:
1/ABC
2/ABC
4/XYZ
5/XYZ
6/XYZ
现在我有了哈希性能查找,但是哈希表中浪费了大量的空间
有什么想法吗?也许只需要使用PLINQ,并希望获得良好的性能?;) 您可以创建双间接查找:
Dictionary<int, int> keys;
Dictionary<int, string> values;
然后查找数据:
return values[keys[3]]; //returns "ABC"
我不确定使用简单的字符串可以节省多少内存,但一旦您超过“ABC”,它应该会有所帮助
编辑
在Dan Tao在下面的评论之后,我回去查看他在问什么。以下代码:
var abc = "ABC";
var def = "ABC";
Console.WriteLine(ReferenceEquals(abc, def));
将向控制台写入“True”。这意味着编译器或运行时(澄清?)正在维护对“ABC”的引用,并将其指定为两个变量的值
在阅读了更多关于Intern
ed字符串的内容后,如果您使用字符串文字填充字典,或者Intern
ing计算字符串,那么实际上执行我的建议所需的空间将比原始字典所需的空间要多。如果您没有使用Intern
ed字符串,那么我的解决方案应该占用更少的空间
最终编辑
如果正确处理字符串,则原始字典中不应存在多余的内存使用,因为您可以将其分配给变量,然后将该引用指定为值(或者,如果需要,因为您可以Intern
它们)
只需确保分配代码包含中间变量分配:
while (thereAreStringsLeftToAssign)
{
var theString = theStringToAssign;
foreach (var i in range)
{
strings.Add(i, theString);
}
}
arootbeer有一个很好的解决方案,但您可能会发现使用它时会感到困惑
另一种选择是使用引用类型而不是字符串,以便指向同一引用
class StringContainer {
public string Value { get; set; }
}
Dictionary<int, StringContainer> values;
var value1 = new StringContainer { Value = "ABC" };
values.Add(1, value1);
values.Add(2, value1);
class StringContainer{
公共字符串值{get;set;}
}
字典值;
var value1=新的StringContainer{Value=“ABC”};
值。添加(1,值1);
增加(2,值1);
它们都将指向StringContainer的同一个实例
编辑:谢谢大家的评论。此方法处理字符串以外的值类型,因此它可能比给定示例更有用。另外,据我所知,字符串的行为并不总是与引用值所期望的方式相同,但我可能错了。使用平衡有序树(或类似的)将范围的开始映射到范围和数据的结束。对于不重叠的范围,这将很容易实现。如果您的字典要真正存储范围广泛的键值,那么将所有可能的范围扩展为显式键值的方法将快速消耗比您可能拥有的内存更多的内存
最好的选择是使用支持某种二进制搜索(或其他O(logn)查找技术)变体的数据结构。下面是一个在内部使用OrderedList并具有O(logn)性能的
要实现恒定时间O(1)查找,需要将所有范围扩展为显式键。这需要大量内存,并且在需要拆分或插入新范围时,实际上会降低性能。这可能不是您想要的。因为,下面的代码不会创建字符串“ABC”的多个实例;相反,它实习一个实例,并将对该实例的引用分配给字典中的每个KeyValuePair
:
var dictionary = new Dictionary<int, string>();
dictionary[0] = "ABC";
dictionary[1] = "ABC";
dictionary[2] = "ABC";
// etc.
假设用户遵循说明并键入“ABC”,上述程序将输出True
,然后输出False
。所以你可能会想,“啊,所以当一个字符串只在运行时提供时,它不是被隔离的!所以这可能是我的值可以被复制的地方!”
但是。。。再说一遍:我不这么认为。这一切都回到了一个事实,即您将为一系列关键点指定一个值。假设您的值来自用户输入;然后,您的代码将如下所示:
keys.Add(1, 1);
keys.Add(2, 1);
keys.Add(3, 1);
//...
keys.Add(11, 3);
values.Add(1, "ABC");
//...
values.Add(3, "LMN");
var dictionary = new Dictionary<int, string>();
int start, count;
GetRange(out start, out count);
string value = GetValue();
foreach (int key in Enumerable.Range(start, count))
{
// Look, you're using the same string instance to assign
// to each key... how could it be otherwise?
dictionary[key] = value;
}
var dictionary=newdictionary();
int开始,计数;
GetRange(超出开始、超出计数);
字符串值=GetValue();
foreach(可枚举范围中的int键(开始,计数))
{
//看,您正在使用同一个字符串实例来分配
//每一把钥匙…怎么可能是别的?
字典[键]=值;
}
现在,如果您实际上想的更多的是什么——您可能有巨大的范围,这使得为该范围内的每个键定义KeyValuePair
是不切实际的(例如,如果您的范围为1-1000000)--那么我同意你最好使用某种基于二进制搜索的数据结构。如果这更符合你的情况,那么就这么说吧,我很乐意在这方面提供更多的想法。(或者您可以只看一下已经发布的链接LBushkin。)对于大范围,您可以使用范围压缩(例如,将某些范围视为块);此外,您可以使用数组(可以很好地打包)作为位掩码或间接查找(请参见arootbeer的答案)?如果您实际上只是在使用数字和字符串,我很好奇.NET是否为您适当地缓存了所有字符串,而您只是在浪费空间来存储所有指针,这可能是不可避免的。否则就有可能
string x = "ABC";
Console.Write("Type 'ABC' and press Enter: ");
string y = Console.ReadLine();
Console.WriteLine(Equals(x, y));
Console.WriteLine(ReferenceEquals(x, y));
var dictionary = new Dictionary<int, string>();
int start, count;
GetRange(out start, out count);
string value = GetValue();
foreach (int key in Enumerable.Range(start, count))
{
// Look, you're using the same string instance to assign
// to each key... how could it be otherwise?
dictionary[key] = value;
}