我想实现一个字典,将唯一的异构数据(变体)与唯一的int
配对,这样我就可以重复int
而不是重复值(可能很大)。如果需要,我会通过字典将其转换为原始值。
数据集很大,因此O(1)
中的(int->数据)非常重要。 (data-> int)和insert / delete应该都是O(log n)
平均情况,因为这些操作不太重要。数据顺序无关紧要,但插入/删除不得使现有的int键无效。
我尝试过哈希表和 SSTable 方法。使用散列表,即使将散列值用作indice,所需的存储也相当高,而不是将其与值一起存储。 Collissions降低了效率,但所有操作的摊销复杂度为O(1)
。另一方面,SSTable提供了更糟糕的操作复杂性并复制了值(一次用于矢量存储,一次用于map-index)。整体内存消耗仅略低于哈希表字典。
词典摘要要求:
有没有办法改进词典的设计,以进一步降低内存需求,同时保留O(1)
int->数据查找和合理的插入/删除/数据 - > int?
答案 0 :(得分:2)
如果int
- >数据速度是最重要的,那么您应该进行设置,以便只进行数组索引操作。
将您的数据对象保存在std::vector<data> forward_map
中。 int-&gt;数据只是一个forward_map[i]
查找,它是O(1),其常数因子尽可能低。
取决于您的&#34;数据&#34;的比较操作。对象支持,可能是二叉搜索树或std::unordered set将是不错的选择。 &#34;值&#34; BST /集的类型只是int
,但根据int
,对这些forward_map[i] < forward_map[j]
的比较实际上比较i < j
,不是。< / p>
所以假设你有一个std::unordered_set< forward_map_reference_t > reverse_map
。 (使用STL容器实际上并不容易,见下文。)
我们实际上将一个集合用作地图:密钥为forward_map[val]
,其值为int val
。
要查找给定int k
的reverse_map条目,您需要实际搜索forward_map[k]
。
const data_t & lookup(int k) { return forward_map[k]; }
int search(const data_t &)
:reverse_map.find()
效率很高。
delete(const data_t &)
:搜索&amp;删除reverse_map条目,返回int k
。将k
添加到forward_map的LIFO免费列表中。 (不要触摸forward_map条目。如果你需要检测使用后的forward_map条目,那么此时将其归零。)insert(const data_t &)
:检查空闲列表的头部是否有重复使用的条目,否则forward_map.push_back()
。 k
=您将条目放在转发地图中的位置。将k
添加到反向地图。为避免存储data_t
项的另一个副本,reverse_map需要在其搜索操作中引用forward_map。
由于缓存未命中,使用基于哈希表而不是搜索树的reverse_map具有潜在的巨大优势。通常,将键与树节点进行比较所需的所有数据都存在于节点中,但在这种情况下,它是对forward_map的引用。不仅可以从reverse_map本身加载cache-miss,因此forward_map [k]也是如此。 (来自未知地址的负载无法提前开始,与无序CPU上的已知地址情况不同,因此这是非常糟糕的)。推测执行可能会从reverse_map开始下一次加载,但事情仍然很糟糕。 哈希表需要的总密钥比较显着减少,这是一个很大的优势。
在这里使用STL容器确实存在鸡和蛋问题:考虑std::unordered_set<int>
:密钥类型为int
。我们使用自定义KeyEqual
功能,根据forward_map[i]
进行比较。但只有.find(const Key& key)
,而不是.find(const data_t&)
。
一个丑陋的解决方法是暂时将data_t
复制到forward_map
中的空闲位置,这样它就会有一个我们可以传递给unordered_set<int, custom_compare>::find
的索引,但这个额外的复制是愚蠢的
另一个错误选项(可能在编译时无法优化)将是一个具有访问data_t
的虚函数的类。地图包含一个具有单个int
成员的类。我们传递.find()
一个也有data_t &
的派生类,并在哈希使用的虚函数的重载中引用它而不是int数组索引。和KeyEquals函数。
您可能必须构建自己的自定义数据结构,或者使用STL之外的其他内容,除非有办法让STL接受与现有集合成员不同类型的密钥。
答案 1 :(得分:0)
您可以使用侵入式链接列表。例如:
foreach
现在,只需存储private void CompressToZip(string fileName, Dictionary<string, byte[]> fileList)
{
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var file in fileList)
{
var demoFile = archive.CreateEntry(file.Key);
using (var entryStream = demoFile.Open())
using (var b = new BinaryWriter(entryStream))
{
b.Write(file.Value);
}
}
}
using (var fileStream = new FileStream(fileName, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
}
,而不是存储struct Node {
Node *prev, *next;
variant<int, float, vector<string>> data;
};
来定位其中一项内容。如果要删除一个:
int
现在,当您拨打Node*
时,它会从列表中消失。
可以预见,Boost的实现具有许多奇特的功能:http://www.boost.org/doc/libs/release/doc/html/intrusive.html