【问题标题】:Why does the simple arithmetic subtraction not work in the "if" condition?为什么简单的算术减法在“if”条件下不起作用?
【发布时间】:2020-07-08 11:57:18
【问题描述】:

我有 2 个数组,其中包含元素。我使用散列来获取每个元素出现多次的频率。 mapcheckA 包含第一个数组元素的频率,mapcheckB 包含另一个数组的频率。我正在尝试从两个无序地图中删除/擦除任何重复的元素。这是执行此操作的代码:

for (auto i : mapcheckA) {
  if (mapcheckB.find(i.first) != mapcheckB.end()) {
    if (i.second >=
        mapcheckB.find(i.first)
            ->second) {  // This block of code doesn't work as expected
      i.second -= mapcheckB.find(i.first)->second;
      mapcheckB.erase(i.first);
    } else {
      mapcheckB.find(i.first)->second -= i.second;
      mapcheckA.erase(i.first);
    }
  }
}

for (auto i1 : mapcheckA) {
  cout << i1.first << "\t" << i1.second << endl;
}

for (auto i : mapcheckB) {
  cout << i.first << "\t" << i.second << endl;
}

当我以这样的方式输入元素时,第一个数组包含的元素频率比第二个数组中的频率高,如下所示:

arrayA = [ 1, 1, 1, 1, 4, 4 ];  // Here frequency of "1" is 4.
arrayB = [ 2, 2, 1, 1, 3, 3 ];  // Here frequency of "1" is 2.

在这种情况下,“if”条件下的代码块不会将频率从 1 更改为 2。 如果我更改数组的顺序,则代码可以正常工作。我在这里做错了什么?

【问题讨论】:

  • 当我打印键值对时,对于上述 2 个数组,输出为: 对于 arrayA:4 2 1 4 对于 arrayB:3 2 2 2
  • 在循环 for (auto i : mapcheckA) 中,您正在对元素的副本进行操作,您的意思是要对实际对象 (for (auto&amp; i : mapcheckA)) 的引用进行操作吗?
  • @AlgirdasPreidžius 在i 之前在for (auto i: mapcheckA) 中插入&amp; 后,问题就解决了。但我不明白为什么以及如何解决它。
  • @BennyK 但它工作正常。当第二个数组中某个元素的频率大于第一个时,输出是正确的。当第一个数组中的频率大于第二个时,我遇到了问题。
  • @Thomas Sablik - 抱歉,错过了“无序地图”部分。

标签: c++ arrays algorithm debugging unordered-map


【解决方案1】:

您有两个重大错误:

  1. 处理数据的副本(已在 cmets 中标识)。使用引用意味着更改将反映在原始容器中,而不是在副本超出范围时被丢弃。

    for (auto& i : mapcheckA) { // 添加 &

  2. 未定义的行为 在您的“其他”情况下,您删除迭代中的当前元素,使迭代器 i 无效,但循环继续增加 i。程序状态不确定,不能这样做。

不幸的是,对于范围广泛的 for 循环,没有好的方法可以做到这一点。您的选择:

  • 记住键(或迭代器)以备后用,并在循环后删除它们

  • 手动滚动循环以进行手动迭代器递增:

for (auto i = begin(mappedA), e = end(mappedA); i != e; ) {
    if (...) {
        ...
        ++i;
    }
    else {
        ...
        i = mappedA.erase(i);
    }
}

此外,您可以考虑使用 c++ 结构化绑定 以使代码更具可读性并减少相同的查找次数。综上所述:

for (auto iterA = begin(mapcheckA), endA = end(mapcheckA); iterA != endA; ) {
    auto& [keyA, valueA] = *iterA;
    if (auto iterB = mapcheckB.find(keyA); iterB != end(mapcheckB)) {
        auto& [keyB, valueB] = *iterB;
        if (valueA >= valueB) {
          valueA -= valueB;
          mapcheckB.erase(iterB);
          ++iterA;
        } else {
          valueB -= valueA;
          iterA = mapcheckA.erase(iterA);
        }
    }
}

注意:if 的两半都会更新 iterA,而不是在 for 循环中进行,因为其中一个使用 ++,而另一个获取调用擦除的结果。

【讨论】:

    猜你喜欢
    • 2020-11-16
    • 2021-08-14
    • 2021-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 1970-01-01
    • 2021-11-10
    相关资源
    最近更新 更多