具有非常快速的int->数据查找和快速反向查找(搜索/插入/删除数据)的压缩字典?

时间:2016-05-07 17:09:26

标签: c++ algorithm performance dictionary

我想实现一个字典,将唯一的异构数据(变体)与唯一的int配对,这样我就可以重复int而不是重复值(可能很大)。如果需要,我会通过字典将其转换为原始值。

数据集很大,因此O(1)中的(int->数据)非常重要。 (data-> int)和insert / delete应该都是O(log n)平均情况,因为这些操作不太重要。数据顺序无关紧要,但插入/删除不得使现有的int键无效。

我尝试过哈希表 SSTable 方法。使用散列表,即使将散列值用作indice,所需的存储也相当高,而不是将其与值一起存储。 Collissions降低了效率,但所有操作的摊销复杂度为O(1)。另一方面,SSTable提供了更糟糕的操作复杂性并复制了值(一次用于矢量存储,一次用于map-index)。整体内存消耗仅略低于哈希表字典。

词典摘要要求:

  • 查找int->数据:O(1)
  • 查找数据 - > int:最差的O(log n)
  • 插入:最差的O(log n)
  • 删除:O(log n)最坏[或其他方式,如垃圾收集,如果不是一直运行可能会表现更差]
  • 可能的最低内存要求

有没有办法改进词典的设计,以进一步降低内存需求,同时保留O(1) int->数据查找和合理的插入/删除/数据 - > int?

2 个答案:

答案 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容器?

在这里使用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