通过删除一些键和一些元素来更新映射的问题

problem with updating a map by removing some keys and also some elements

本文关键字:更新 映射 问题 删除 元素      更新时间:2023-10-16

我有一张地图。假设是map<int, vector<int> > mymap1。我想通过删除一些"键"来更新mymap1,并从所选键的矢量部分删除不需要的"元素"。要删除的"键"或"元素"来自另一个称为"mylabel"的向量。实际上,我需要在映射中保留的是标签等于1的值。(最后,键必须包含标签为1的元素。)

我已经实现了这个(见下面的代码),但是得到了一些编译器错误。

map<int, vector<int> > mymap1;
map<int, vector<int> >::iterator map1;
for (map1=mymap1.begin();map1!=mymap1.end();map1++){
       int key = map1->first;
       if (mylabel[key].Label() != 1){ mymap1.erase(key);
       }
       else{
            vector<int> &myvec = map1->second;
            for (vector<int>::iterator rn=myvec.begin(); rn!=myvec.end(); rn++){
                 if (mylabel[*rn].Label() != 1) myvec.erase(myvec.begin()+(*rn));
            }                        
       }
}

为了让你了解,我展示了一些我的地图的例子。

0 1 2 6 10
1 0 2 4 3 6
2 0 1 3 5 8
3 1 2 4 5 7
4 1 3 6 7
5 2 3 8 7 9
6 1 0 7 4
7 6 4 3 5 9 11 10 13 12
8 2 5 9 11 18 15 19 20 22
9 5 7 11 8
10 0 7 14 16
11 9 7 8 13
12 7 13 14
13 7 12 11 14 15
14 12 10 16 13 15 17
15 13 14 8 17 19
16 14 10 17 21
17 14 16 15 21 18
18 8 20 19 17 26 27
19 8 15 18
20 8 18
21 16 17 23 24
22 8
23 25 21 24 26
24 23 21
25 23 26
26 23 25 18
27 18 28
28 27

如果我给你看我的mylabel,它是这样的。

for(int c=0;c<mylabel.size();c++){
    cout<<c<<" : "<<"label "<<mylabel[c].Label()<<endl;
}
0 : label 0
1 : label 0
2 : label 0
3 : label 0
4 : label 0
5 : label 1
6 : label 0
7 : label 1
8 : label 0
9 : label 1
10 : label 0
11 : label 1
12 : label 0
13 : label 0
14 : label 1
15 : label 1
16 : label 1
17 : label 1
18 : label 0
19 : label 0
20 : label 0
21 : label 1
22 : label 0
23 : label 0
24 : label 0
25 : label 1
26 : label 1
27 : label 0
28 : label 0

当我停用else部分并运行上面的代码时,我得到了一个输出。但是,我想告诉你,这是一个错误的结果。我得到了应该被删除的额外的键。我不明白为什么会得到这个错误的结果。如果我显示键列表我得到了什么

5
7
9
11
14
15
16
17
20 - wrong
21
24 - wrong
25
26

你能帮我纠正我的代码,以便得到我修改后的地图吗?

您的擦除逻辑是错误的,您最终使用了无效的迭代器。(如果你删除一个迭代器,然后继续使用它,你实际上是在把地毯从你的脚下拉出来。)

对于基于节点的容器(list、map、set、unordered),通常按如下方式擦除:

for (auto it = c.begin(); it != c.end(); )
{
  if (must_delete(*it))  // or it->first
  {
    c.erase(it++); // advance first, then erase previous
  }
  else
  {
    ++it;
  }
}

(这种模式是我最喜欢的对后缀自增操作符的解释)

对于连续的容器(vector, deque),一次擦除一个元素是低效的,因为它会导致重复移动。这里首选的习惯用法是"remove/erase",但如果您不想直接按元素值删除,则需要提供合适的谓词。为简洁起见,下面是一个使用lambdas的示例:

std::vector<int> v;
v.erase(std::remove_if(v.begin(), v.end(),
                       [](int n)->bool{return some_criterion(n);}),
        v.end());

在你的情况下,你可以把写成[mylabel&](n)->bool{ return mylabel[n].Label() != 1; };如果没有lambda,也可以写一个传统的谓词对象:

struct LabelFinder
{
  LabelFinder(const LabelVector & lv) : label(lv) { }
  inline bool operator()(int n) const
  {
    return label[n].Label() != 1;
  }
private:
  const LabelVector & label;
};

现在使用:

v.erase(std::remove_if(v.begin(), v.end(), LabelFinder(mylabel)), v.end());

问题在for循环。std::vector<T>::erase()返回迭代器到新位置,后面跟着被擦除的项。所以循环应该写成:

for (vector<int>::iterator rn=myvec.begin(); rn!=myvec.end();)
{
       if (mylabel[*rn].Label() != 1) 
             rn = myvec.erase(rn);
       else
             ++rn;
}   

阅读文档:

  • 向量:消除()

顺便说一下,我对此表示怀疑:

 rn = myvec.erase(myvec.begin()+(*rn));
            Vs
 rn = myvec.erase(rn);

你确定要第一个吗?


擦除不等于1的元素的惯用方法是:

 //Define this function
 bool isNotOne(int n) {  return n != 1; }
//then do this instead of writing manual loop
 myvec.erase( remove_if(myvec.begin(), myvec.end(), isNotOne), myvec.end() ); 

它叫做:

    <
  • Erase-Remove成语/gh>