循环挂起迭代的 std::擦除 on std::list

loop hangs for iterated std::erase on std::list

本文关键字:std on list 擦除 挂起 循环 迭代      更新时间:2023-10-16

我正在尝试使用哈希表删除存储在列表中的整数向量的重复组合。遍历列表中的每个整数向量,I:

  1. 计算hash_value(塔什(
  2. 查看哈希值是否已在哈希表中(pids(
  3. 如果它在哈希表中,请从列表中擦除该向量。 否则,将该值添加到hash_table并递增列表 迭 代

打印语句似乎证实了我的逻辑,但循环在迭代的第四步挂起。我已经注释了导致问题的it++vz.remove(it),并且仅在下面的代码中显示逻辑。该代码也可通过 ideone: https://ideone.com/JLGA0f

#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}

问题是这行代码:

vz.erase(it);

它将迭代器保留在原来的位置,即使其无效。它应该是:

vz.erase(it++);

it = vz.erase( it );

注意:std::unoredered_set::insert()返回值告诉您插入是否成功(如果已经存在相同的值元素(,则应调用它并检查结果。在代码中,您执行两次查找:

if (pids.insert(thash).second ) { 
// new element added
++it;
} else { 
// insertion failed, remove 
it = vz.erase( it );
}

正如std::list提供的那样remove_if()可以简化您的代码:

vz.remove_if( [&pids,&lprimes]( auto &v ) { 
return !pids.insert( hash_cz(v, lprimes) ).second );
} );

而不是整个循环。

如果元素已经被看到,你擦除((it节点,然后在循环结束时递增it:未定义的行为。尝试擦除(it++(。

如果元素没有被看到,你递增it然后在for结束时再次执行此操作,如果it在它经过末端时被end() - 1,则产生UB。