遍历 STL 映射(集/多集)的最佳方法,同时元素可能会在循环期间被删除并重新插入?

Best way to traverse a STL map(set/multiset) while the element might be removed and inserted back during the loop?

本文关键字:循环 删除 新插入 插入 元素 多集 映射 STL 最佳 遍历 方法      更新时间:2023-10-16

我只是注意到这个函数对于实现DFS搜索算法似乎非常有用。

例如,我们知道遍历图形 {{A->B}、{B->C}、{A->C}、{C->A}} 的边,并且我们希望找到一条跨越所有边的路径:A->C->A->B

在我看到这种问题的时候,我将通过DSP来解决它,通过一个数据结构来表示已经看到/使用了哪个"节点/边缘"。

我通常只是使用一个向量来保存它并修改值(并修改回来(以模拟使用的节点

例如:

string now = "A";
vector<string> nexts = get_all_edges_starting_from(now);
for (int i=0; i<nexts.size(); i++) {
string next = nexts[i];
nexts[i] == "visited"; // assume no node named "visited"
if (go_recursive_and_find_path_cover_all_edges())
{
results.push_back(now); // some global or whatever variable to store the result
return true;
}
nexts[i] = next; // abandon the "visit" so the recursion can use node/edge next time
}
return false;

它可以找到,但与使用地图或集合/多集相比效率不高。但是,我真的不知道如何在循环期间听到元素并重新插入时遍历映射/集/多集。

例如,失败的示例可能是:

string now = "A";
multiset<string> nexts = get_all_edges_starting_from(now);
multiset<string>::iterator it = nexts.begin();
for (it != nexts.end()) {
string next = *(it);
nexts.erase(next); // visited
if (go_recursive_and_find_path_cover_all_edges())
{
results.push_back(now); // some global or whatever variable to store the result
return true;
}
nexts.insert(next); // abandon the "visit"
it++; 
}
return false;

此示例将失败,因为我通过在循环内删除和添加元素来破坏元素的内存结构。

一般来说,我知道这是不可能的,因为当我们考虑在当前遍历 posisiton 之前添加"新"元素的情况时,可能没有定义行为。但是,在DFS的示例中,我们通常只是删除元素并添加元素"在返回之前被删除"。

有没有简单的方法可以做到这一点?

首先,目前尚不清楚多映射是否真的比向量好,因为它的内存局部性要差得多,因此请尝试两种实现策略并进行比较。

接下来,您的循环包含一些我们可以修复的基本效率低下:

for (auto it = nexts.begin(); it != nexts.end(); )
{
string next = *it;
auto it = nexts.erase(it);
if (...) { ... }
nexts.insert(it, std::move(next));
}

兴趣点:

  • 不要按值删除,而是按迭代器删除。
  • 插入带有提示,因为您已经知道元素的去向。
  • 移动插入字符串,因为您不需要副本。
  • 在 C++17 中,您甚至可以使用extract将字符串移地图。