创建按顺序索引的有序映射

Posted

技术标签:

【中文标题】创建按顺序索引的有序映射【英文标题】:Creating a Sequentially Indexed Ordered Map 【发布时间】:2017-12-31 07:58:03 【问题描述】:

以下代码显示了我想创建的一个非常基本但必不可少的功能,并且目前我无法自己调试一些令人讨厌的运行时错误。我一直在努力为我正在寻找的东西写一个解决方案,这是我最接近的。非常感谢您在确定此实现的修复或重新设计方面的任何帮助!

这是课程。我正在寻找的是一张地图,它仍然可以执行其 operator[key] 功能,但也可以在添加元素时按顺序迭代。我试图通过使用一个查找映射来做到这一点,它的值是指向对应对向量中的实际值的指针。

template <typename t>
class IndexedMap 
 public:
  t& operator [] (string s) 
    bool nu = true;

    for (auto& e : actual) // for each pair
      if  (e.first == s) // if exist
    nu = false; 
    if (nu == true)  // if needs created
      actual.push_back(pair <string, t>()); // create proper pair
      actual.back().first = s; // assign key
      // create copy in map @ same key pointing to actual value
      lookup[s] = &actual.back().second;
    
    return *lookup[s]; // return reference to value
  

  typename vector <pair <string, t>>::iterator begin () 
    return actual.begin();
  

  typename vector <pair <string, t>>::iterator end () 
    return actual.end();
  

 private:
  vector <pair <string, t>> actual;
  map <string, t*> lookup;
;

此实现与以下 test.cpp “工作”- 意味着它将运行并且我确实看到了我正在寻找的结果,但是在退出 test.cpp 时,我遇到了一些涉及调用 free 的疯狂错误() 我不确定这是如何发生的或如何解决的。

test.cpp:

int main () 
  IndexedMap <vector <int>> test;

  test["BILLS"]; test["GAS"]; 
  test["GROCERY"]; test["PETS"]; 
  test["TAKEOUT"]; test["OTHER"];

  int i = 0;
  for (auto e : test) // check order
    cout << e.first << endl;
  for (auto& e : test) // assign 3 unique values to each vector
    for (int f = 0; f < 3; ++f, ++i)
      e.second.push_back(i);
  for (auto e : test)  // display them
    cout << e.first << ":" << endl;
    for (auto f : e.second)
      cout << f << endl;
  
  vector <int> blank; // test modifying a value
  test["GAS"] = blank;
  for (auto e : test["GAS"])
    cout << e << endl;
  cout << "hopefully empty?" << endl;

我希望这不会让我解释或写出来的方式过于混乱。非常感谢您提供的任何帮助。

大家新年快乐!

【问题讨论】:

单个 SO 问题的代码有太多错误。您应该将问题集中在一个明确的问题上,并发布相关的minimal reproducible example。此外,在 cmets 中已经解释了一个较早的问题,为什么存储指向向量元素的指针不起作用。 @juanchopanza 我回头看,实际上找不到足够清楚的指示为什么指针不起作用,这就是我正在寻找这个问题的简明解释的主要原因。我认为我的问题的范围很有限...... 所以这条评论包含重要信息:“更好地映射到向量的索引,因为指针很容易失效”。然后你要做的是研究向量指针或迭代器失效。 @JosephJerrel 关于指针:***.com/questions/46991224/… @juanchopanza 哇,我真的找到了一个可行的解决方案!它花了我一整天的时间,现在感谢你的神秘建议和不完整的帮助建议,我已经到达了工作实施。大声笑,所以谢谢你先生。你把我引向了正确的方向,我想通了,所以没有讽刺——我真的很感激。 【参考方案1】:

感谢@juanchopanza 的帮助,我为这个问题想出了一个可行的解决方案。

仍然不确定在上面发布的先前实现中指针在何处或如何失效,但现在通过使用索引来标识向量中的正确元素,然后返回该元素本身,而不是指向该位置的指针我我很安全;)

t& operator [] (string s) 
    bool nu = true;

    for (auto& e : actual) // for each pair
      if  (e.first == s) // if exist
        nu = false; 
    if (nu == true)  // if needs created
      actual.push_back(pair <string, t>()); // create proper pair
      actual.back().first = s; // assign key
      lookup[s] = actual.size()-1; // assign proper index in map @ same key 
    
    return actual[lookup[s]].second; // return reference to value
  

【讨论】:

查看我的回答,了解原因。 一个向量需要连续分配它的元素,所以当它增长时,它可能不得不重新分配它的内部数组在不同的地址。当它发生时,所有指向向量元素的指针都会变成 dangling. @SergeBallesta 我确实考虑过这一点,但是因为我的向量只增长到 6 的大小,而且这一切都是在使用指针进行任何调用之前完成的,我不认为重新分配是导致失效的原因. 下一步:不要自己做(查找效率低下),而是使用已有的地图。【参考方案2】:

这一行暴露了错误

actual.push_back(pair <string, t>()); // create proper pair

这就是痛苦的根源。

lookup[s] = &actual.back().second; // a pointer to an element in a vector.

当向量调整大小时会发生什么?为向量分配一个新数组,并将旧数组中的数据复制到新数组中,并释放旧数组。这会使指向旧数组的指针无效。

让我们稍微评估一下您的解决方案,您遍历向量以查看是否存在 s,这是 O(N),如果发现您在地图中创建一个 lookup,无论如何都是 O(log N)。

我们想使用实际的索引而不是指针,所以你的映射对应该是

using mappair = std::pair<std::string,  int>;

所以如果我们重写(未经测试的代码)

for (auto& e : actual) // for each pair
  if  (e.first == s) // if exist
    nu = false; 
if (nu == true)  // if needs created
  actual.push_back(pair <string, t>()); // create proper pair
  actual.back().first = s; // assign key
  // create copy in map @ same key pointing to actual value
  lookup[s] = &actual.back().second;

return *lookup[s]; // return reference to value

auto found = lookup.insert(mappair(s, -1));
if (found.second)  // true if new element
  actual.emplace_back(mappair(s,t());
  found->first.second = actual.size()-1;

return *found.first;

【讨论】:

非常感谢您的帮助!关于悬空指针 - 我确实考虑过这一点,但是因为我的向量只增长到 6 的大小,而且这一切都是在使用指针的任何调用之前完成的,我不认为重新分配是导致失效的原因。我认为只有当容器继续增长时才会出现这种情况。 @JosephJerrel,在添加 6 个元素期间,它可能会增长 6 倍,具体取决于实现,通常它会以因子 sqrt(2) 增长,因此 1,2,3,5,7在你的情况下。

以上是关于创建按顺序索引的有序映射的主要内容,如果未能解决你的问题,请参考以下文章

数据类型分类

Innodb存储表结构

java集合类

支持按索引和键随机访问、以对数时间插入、删除并保持顺序的数据结构

分块查找

数组的使用