C++ 在std::map中更改元素键的最快方法是什么
我理解人们不能这么做的原因(再平衡等等): 但到目前为止,更改键的唯一方法(我知道)是将节点一起从树中删除,然后使用不同的键插入值:C++ 在std::map中更改元素键的最快方法是什么,c++,performance,dictionary,binary-tree,std,C++,Performance,Dictionary,Binary Tree,Std,我理解人们不能这么做的原因(再平衡等等): 但到目前为止,更改键的唯一方法(我知道)是将节点一起从树中删除,然后使用不同的键插入值: iterator i = m.find(33); if (i != m.end()) { value = i->second; m.erase(i); m[22] = value; } 对我来说,这似乎效率很低,原因更多: 遍历树三次(+平衡),而不是两次(+平衡) 值的另一个不必要副本 不必要的取消分配,然后在树内重新分配节点 我发现
iterator i = m.find(33);
if (i != m.end())
{
value = i->second;
m.erase(i);
m[22] = value;
}
对我来说,这似乎效率很低,原因更多:
您可以省略值的复制 你不能 正如你所注意到的,这是不可能的。映射是有组织的,因此可以有效地更改与键关联的值,但不能更改相反的值
您可以看看Boost.MultiIndex,尤其是它的。Boost.MultiIndex容器具有高效更新功能。STL映射中的键必须是不可变的
如果配对的关键端有那么多的波动性,可能一个或多个不同的数据结构更有意义。您应该将分配留给分配器。:-) 正如你所说,当关键因素发生变化时,可能会有很多再平衡。树就是这样工作的。也许22是树中的第一个节点,33是最后一个节点?我们知道什么 如果避免分配是很重要的,也许你应该试试向量或deque?它们以较大的块进行分配,因此可以节省对分配器的调用次数,但可能会浪费内存。所有容器都有它们的权衡,由您决定哪一个容器在每种情况下具有您所需要的主要优势(假设这很重要) 对于喜欢冒险的人:
如果您确实知道更改密钥不会影响顺序,并且您从未犯过错误,那么一点const_cast无论如何都会让您更改密钥。大约18个月前,我在这里提出了关联容器的算法: 请查看标记为[2009-09-19]的评论,霍华德补充道: 当时我们离FDI太近了,不能考虑这种变化。然而,我认为它非常有用(你显然同意),我想把它加入到TR2中。也许你可以通过发现并通知C++国家机构的代表来帮助你发现这是你想看到的一个特性。 更新 这还不确定,但我认为我们很有可能在C++17中看到此功能:-) 在C++17中,新函数允许您更改密钥。
例如:
std::map m{{10,“土豆”}、{1,“香蕉”};
自动节点句柄=m.extract(10);
nodeHandler.key()=2;
m、 插入(标准::移动(节点句柄));//{{1,“香蕉”},{2,“土豆”}
如果您知道新密钥对地图位置有效(更改它不会更改顺序),并且您不想在地图中删除和添加项目的额外工作,您可以使用常量转换来更改密钥,如下面的unsafeUpdateMapKeyInPlace
:
template <typename K, typename V, typename It>
bool isMapPositionValidForKey (const std::map<K, V>& m, It it, K key)
{
if (it != m.begin() && std::prev (it)->first >= key)
return false;
++it;
return it == m.end() || it->first > key;
}
// Only for use when the key update doesn't change the map ordering
// (it is still greater than the previous key and lower than the next key).
template <typename K, typename V>
void unsafeUpdateMapKeyInPlace (const std::map<K, V>& m, typename std::map<K, V>::iterator& it, K newKey)
{
assert (isMapPositionValidForKey (m, it, newKey));
const_cast<K&> (it->first) = newKey;
}
模板
bool ismappositionvalidworkey(const std::map&m、It、K键)
{
if(it!=m.begin()&&std::prev(it)->first>=key)
返回false;
++它;
返回它==m.end()| it->first>键;
}
//仅在密钥更新未更改映射顺序时使用
//(仍大于上一个键,低于下一个键)。
模板
void unsafeUpdateMapKeyInPlace(const std::map&m,typename std::map::iterator&it,K newKey)
{
断言(IsMappositionValidWorkey(m、it、newKey));
const_cast(it->first)=newKey;
}
如果希望解决方案仅在有效时就地更改,否则会更改地图结构,请执行以下操作:
template <typename K, typename V>
void updateMapKey (const std::map<K, V>& m, typename std::map<K, V>::iterator& it, K newKey)
{
if (isMapPositionValidForKey (m, it, newKey))
{
unsafeUpdateMapKeyInPlace (m, it, newKey);
return;
}
auto next = std::next (it);
auto node = m.extract (it);
node.key() = newKey;
m.insert (next, std::move (node));
}
模板
void updateMapKey(const std::map&m,typename std::map::iterator&it,K newKey)
{
if(IsMappositionValidWorkey(m、it、newKey))
{
未更新的MapKeyInplace(m、it、newKey);
返回;
}
自动下一步=标准::下一步(it);
自动节点=m.extract(it);
node.key()=newKey;
m、 插入(下一步,std::move(节点));
}
是的,效率很低。如果不适合使用,请使用不同的数据结构case@sehe,我不认为这是数据结构的问题,如果我要创建自己的,我会得到相同的红黑树,唯一的区别是它有一个方法,可以重用节点,而不是分配和重新分配。@Chowlett,谢谢,我会记住这一点。“1.遍历树三次(+平衡)而不是两次(+平衡)”-它是两次而不是一次。。。end()
@TonyDelroy不需要遍历。我相信操作符[]是第三个。感谢您的输入,但问题并不在于使用哪种数据结构。相反,问题是(如果我原来的答案是否定的):如果理论上似乎没有理由不应该有API,那么为什么没有API来有效地执行它呢。伪算法简单:找到节点;把它从树上取下来;再平衡;更改分离节点中的键;插回;再平衡@彼得-也许重新键入不是地图上的基本操作?我认为它还可以避免密钥必须是可分配的,这样就可以使用一些额外的类型作为密钥。@Viktor,嗯,第二个例子
std::map<int, std::string> m{ {10, "potato"}, {1, "banana"} };
auto nodeHandler = m.extract(10);
nodeHandler.key() = 2;
m.insert(std::move(nodeHandler)); // { { 1, "banana" }, { 2, "potato" } }
template <typename K, typename V, typename It>
bool isMapPositionValidForKey (const std::map<K, V>& m, It it, K key)
{
if (it != m.begin() && std::prev (it)->first >= key)
return false;
++it;
return it == m.end() || it->first > key;
}
// Only for use when the key update doesn't change the map ordering
// (it is still greater than the previous key and lower than the next key).
template <typename K, typename V>
void unsafeUpdateMapKeyInPlace (const std::map<K, V>& m, typename std::map<K, V>::iterator& it, K newKey)
{
assert (isMapPositionValidForKey (m, it, newKey));
const_cast<K&> (it->first) = newKey;
}
template <typename K, typename V>
void updateMapKey (const std::map<K, V>& m, typename std::map<K, V>::iterator& it, K newKey)
{
if (isMapPositionValidForKey (m, it, newKey))
{
unsafeUpdateMapKeyInPlace (m, it, newKey);
return;
}
auto next = std::next (it);
auto node = m.extract (it);
node.key() = newKey;
m.insert (next, std::move (node));
}