在std::map中修改元素键的最快方法是什么?
What is the fastest way to change a key of an element inside std::map
我理解为什么不能这样做(平衡和东西):
iterator i = m.find(33);
if (i != m.end())
i->first = 22;
但是到目前为止,改变键的唯一方法(我知道)是从树中删除节点,然后用不同的键插入值:
iterator i = m.find(33);
if (i != m.end())
{
value = i->second;
m.erase(i);
m[22] = value;
}
这对我来说似乎效率很低,原因更多:
遍历树三次(+ balance)而不是两次(+ balance)
又一个不必要的值
拷贝不必要的释放,然后重新分配树内的节点
我发现分配和再分配是这三者中最差的。是我遗漏了什么,还是有更有效的方法?
我认为,在理论上,这应该是可能的,所以我不认为改变一个不同的数据结构是合理的。下面是我想到的伪算法:
在树中找到我想要更改密钥的节点
将其从树中分离(不要deallocate)
- 平衡
更改分离节点内的密钥
将节点插入树
- 平衡
在c++ 17中,新的map::extract
函数允许您更改密钥。
例子:
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" } }
可以省略value;
const int oldKey = 33;
const int newKey = 22;
const iterator it = m.find(oldKey);
if (it != m.end()) {
// Swap value from oldKey to newKey, note that a default constructed value
// is created by operator[] if 'm' does not contain newKey.
std::swap(m[newKey], it->second);
// Erase old key-value from map
m.erase(it);
}
我在18个月前就提出了你的关联容器算法:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html # 839
查找标记为:[2009-09-19 Howard add:]的注释。
当时,我们太接近FDIS而没有考虑这一变化。然而,我认为它非常有用(你显然同意),我想把它纳入TR2。也许你可以找到并通知你的c++国家机构代表,这是一个你希望看到的特性。
这还不确定,但我认为我们很有可能在c++ 17中看到这个特性!: -)
要求STL映射中的键是不可变的。
似乎如果你在配对的关键端有那么大的波动性,那么不同的数据结构或结构可能会更有意义。
你不能。
如你所见,这是不可能的。通过组织映射,可以有效地更改与键相关联的值,但不能相反。你看一下Boost。MultiIndex,特别是它的模拟标准容器部分。提振。
您应该将分配留给分配器。: -)
正如你所说,当键改变时,可能会有很多重新平衡。这就是树的工作方式。也许22是树中的第一个节点,33是最后一个节点?我们知道什么?
如果避免分配很重要,也许你应该尝试vector或deque?它们以更大的块进行分配,因此可以节省对分配器的调用次数,但可能会浪费内存。所有容器都有各自的优缺点,由您决定在每种情况下哪种容器具有您需要的主要优势(假设这很重要)。
对于喜欢冒险的人:
如果您确信更改键不会影响顺序并且您永远不会出错,那么const_cast 将让您无论如何都可以更改键。
如果您知道新键对映射位置是有效的(更改它不会改变顺序),并且您不想要将项目删除并添加到映射中的额外工作,您可以使用const_cast
来更改键,如下面的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;
}
如果你想要一个解决方案,只在适当的时候改变,否则改变映射结构:
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));
}
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 通过JNI传递数据数组的最快方法是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 在另一个类视图中添加最多2个图表的正确方法是什么
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 在C++中包含原型文件的正确方法是什么?
- 在 OpenCV C++ 中估计基本矩阵之前对相应点进行归一化的正确方法是什么?
- 在PostgreSQL中根据它们的ID选择大量行的最快方法是什么?
- 在OSX上使用CMake将Adobe的XMP工具包构建为共享库的最简单方法是什么?
- 将一系列整数放入类的最佳方法是什么?
- 从长整整转换为uint64_t的推荐方法是什么?
- 将此布尔值传递给此函数的最有效方法是什么?
- 通过比较C++中的行在 txt 文件中搜索的最简单方法是什么?