让我们考虑一下这段代码:
std::map< int, char > charMap;
for( auto& i : charMap )
{
charMap[ i.first + 1 ] = charMap[ i.first ];
charMap.erase( i.first );
}
让我们说地图有一些带有randomed键的值。我试图将键移动1。 这不会起作用,因为循环会永远持续下去。 有没有一种快速的方法让它发挥作用?
答案 0 :(得分:5)
在C ++ 17中,您可以使用节点提取和拼接(参见P0083R3):
std::map<int, char> tmpMap;
for (auto it = charMap.begin(); it != charMap.end(); )
{
auto nh = charMap.extract(it++); // node handle
++nh.key();
tmpMap.insert(tmpMap.end(), std::move(nh));
}
tmpMap.swap(charMap);
循环提取连续的映射节点,对其进行变异,然后将节点重新插入tmpMap
(现在使用不同的密钥)。最后,charMap
为空,tmpMap
包含所有修改了密钥的元素,因此我们将两者交换。
在C ++ 17之前,您必须复制(或移动)值数据以插入带有新密钥的新元素。
std::map<int, char> tmpMap;
for (auto & p : charMap)
tmpMap.emplace_hint(tmpMap.end(), p.first + 1, std::move(p.second));
tmpMap.swap(charMap);
这需要节点的内存分配,因此新的基于拼接的解决方案更有效。
在任何一种情况下,我们都可以使用提示插入,因为我们以相同的顺序重建元素,因此最新的元素总是插在最后。
答案 1 :(得分:3)
您可以简单地选择从最后一个元素开始的向后迭代:
for( auto pi = charMap.end(); pi-- != charMap.begin(); pi=charMap.erase( pi ))
charMap[ pi->first + 1 ] = charMap[ pi->first ];
<强> Online demo 强>
这不会永远循环,因为您插入的新元素将始终位于当前元素之后,因此不会一次又一次地重新处理。
对于更一般的转型,您无法确定对元素排序的影响,我宁愿选择std::transform()
:
std::map<int, char> tmp;
std::transform(charMap.begin(), charMap.end(), std::inserter(tmp,tmp.begin()),
[](auto e) { return std::make_pair(e.first+1, e.second); });
std::swap(tmp, charMap); // the old map will be discarded when tmp goes out of scope
<强> Online demo 强>
答案 2 :(得分:0)
您不能使用这种范围迭代有两个基本原因:
第一个原因是地图的基本属性是迭代地图按关键顺序迭代。
您正在迭代地图。因此,如果映射中的第一个键是键0,则将值复制到键1.然后,迭代到映射中的下一个键,即刚刚创建的键1,然后将其复制到键2.泡沫,冲洗,重复。
有几种方法可以解决这个问题,但由于地图的第二个基本方面,这些都不重要:
charMap[1]=charMap[0];
这可以应对charMap[0]
到charMap[1]
。它对charMap[0]
没有任何作用。它仍然存在。什么也没发生。因此,假设地图中的最低键为0,并且您正确地移动了键,您仍然会在地图中使用键0输入一个值。同样适用于地图中的其他所有内容。
但是,让我们说你解决了可以解决的几种方法之一的第一个问题。然后,让我们说你的地图有0,5和7键的值。
将密钥#0复制到密钥#1,将密钥#5复制到密钥#6,将密钥#7复制到密钥#8后,拿一支纸和一支铅笔,找出你现在在地图中的内容。
答案:它不会是键1,6和8.它将是键0,1,5,6,7和8.
您所做的只是将每个值复制到下一个键。这是因为计算机完全按照你的要求去做,不多也不少。计算机无法按照您的意愿执行操作。
最简单的方法是创建一个新地图,并使用更新的键值将旧地图的内容复制到新地图。您仍然可以使用范围迭代。然后,用新地图替换旧地图。
当然,如果地图非常大,这就变得不切实际了。在这种情况下,仍然可以在不使用第二个映射的情况下执行此操作,但算法会有些复杂。胶囊摘要是:
1)以相反的顺序迭代键。不能在这里使用范围迭代。
2)将密钥复制到地图中的下一个值后,显式删除其原始密钥中的值。