【问题标题】:In C++, how can I delete all zeros except for x of them in every run of consecutive zeros within a list?在 C++ 中,如何在列表中的每次连续零运行中删除除 x 之外的所有零?
【发布时间】:2012-12-29 23:17:26
【问题描述】:

对于 C++ 列表中每次运行 x 或多个连续零,我想删除运行中除 x 之外的所有零。如果x = 0,则删除所有零。

我在想一个 C++ 函数,它接受一个列表 list<int> L 和一个数字 int x 作为输入。

例如,让L = {7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 0, 8}

  • 如果x = 0,则返回L = {7, 12, 2, 27, 10, 8}
  • 如果x = 1,则返回L = {7, 0, 12, 0, 2, 0, 27, 10, 0, 8}
  • 如果x = 2,则返回L = {7, 0, 12, 0, 0, 2, 0, 0, 27, 10, 0, 0, 8}
  • 如果x = 3,则返回L = {7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 8}
  • 如果x = 4,则返回L = {7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 0, 8}(与原L相同)
  • 如果是x >= 5,则返回原始L,因为不存在5 个或更多连续零的运行。

几个月前,我使用 Python (stackoverflow.com/questions/11732554/...) 提出了上述相同的问题,并得到了很好的答案。现在我想用 C++ 完成这个任务。

任何帮助将不胜感激。

【问题讨论】:

  • 使用与 Python 相同的算法。
  • C++ 是否有与yield 等效的功能?
  • 你可以直接 push_back 到你的结果列表而不是 yield。

标签: c++ algorithm list c++11 erase-remove-idiom


【解决方案1】:

对于案例 0,您可以使用 std::remove,对于案例 1,您可以将 std::unique 与仅适用于 0 的谓词一起使用。对于更大的值,要么设计一个偷偷摸摸的有状态谓词来与unique 一起使用,要么借用它的逻辑来应用于更大的序列。

【讨论】:

    【解决方案2】:

    这里有一些代码应该可以完成这项工作:

    void DeleteAllZerosInARow(std::list<int>& theList, int x)
    {
        if(x == 0)
        {
            theList.remove(0);
            return;
        }
    
        int streak = 0;
        std::list<int>::iterator itor = theList.begin();
        while(itor != theList.end())
        {
            if(*itor == 0)
                ++streak;
            else
                streak = 0;
    
            if(streak > x)
                itor = theList.erase(itor);
            else
                ++itor;
        }
    }
    

    基本上,您计算一行中有多少个零,如果您是&gt; x,则将其删除,否则继续迭代列表。

    给出以下输出:

    • 0:7,12,2,27,10,8
    • 1:7,0,12,0,2,0,27,10,0,8
    • 2:7,0,12,0,0,2,0,0,27,10,0,0,8
    • 3:7,0,12,0,0,2,0,0,0,27,10,0,0,0,8
    • 4:7,0,12,0,0,2,0,0,0,27,10,0,0,0,0,8
    • 5:7,0,12,0,0,2,0,0,0,27,10,0,0,0,0,8

    这取决于你的风格,remove_if 可能是更多的C++ish 方式,但我发现直接操作值更清晰,它不涉及新的数据类型(struct跟踪您遇到的0 的数量)。

    代码不能使用NTL::ZZ 的原因仅仅是int0NTL::ZZ 大数之间没有隐式转换,因此它不能remove(0)。你可以做的事情可能是这样的:

    if(x == 0)
    {
        static ZZ zero; // default value is 0, static so that it is only constructed once
        theList.remove(zero); // remove all items who are equal to "zero"
        return;
    }
    

    【讨论】:

    • 我不认为remove_if 实际上保证它测试元素的顺序,所以我更喜欢滚动你自己的,以防万一它没有:-)
    • @emartel,虽然我没有在上面的示例中展示它,但我的列表实际上包含大整数,所以我在 NTL C++ 库中使用ZZ 类。当通过将list&lt;int&gt; 替换为list&lt;ZZ&gt; 来尝试上面的代码时,会引发编译时错误:no matching function for call to std::list&lt;NTL::ZZ, std::allocator&lt;NTL::ZZ&gt; &gt;::remove(int)。有没有办法可以将上面的代码修改为ZZ 列表?
    • @b_ron_ 我添加了一些解释,如果有帮助请告诉我!
    • @emartel,非常感谢。您的解释非常有帮助,正是我编译代码所需要的。
    【解决方案3】:

    最简单的方法是返回 std::vector&lt;int&gt; 并使用 push_back,这样您就不必担心分配正确大小的数组。

    template<typename Iter>
    std::vector<int> filter_zeroes(Iter start, Iter end, const size_t num_zeroes)
    {
        std::vector<int> output;
        size_t zero_count = 0;
        while (start != end)
        {
            if (*start != 0)
            {
                output.push_back(*start);
                zero_count = 0;
            }
            else if (zero_count < num_zeroes)
            {
                output.push_back(*start);
                ++zero_count;
            }
            ++start;
        }
    }
    

    你可以让这个方法更通用。将int 更改为typename ValueType 并将0 更改为ValueType value_to_remove,然后您就可以达到std::algorithm 通用级别...

    【讨论】:

      【解决方案4】:

      您可以通过将仿函数传递给 list::remove_if 来实现。下面的例子。

      #include <iostream>
      #include <list>
      
      std::list<int> origL{7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 0, 8};
      
      template <typename T>
      struct remove_more_than_n_consecutive_zeros
      {
          int n;
          int i;
          F(int n) : n(n), i(0) { }
      
          bool operator()(const T &element) {
              if (0 == element) {
                  ++i;
                  return i > n;
              }
              else 
              {
                  i = 0;
                  return false;
              }
          }
      };
      
      int main()
      {
          for (int i = 0; i < 5; ++i) {
              std::list<int> L = origL;
              L.remove_if(remove_more_than_n_consecutive_zeros<int>(i));
              for (int x : L) { std::cout << x << " "; }
              std::cout << std::endl;
          }
      }
      

      【讨论】:

        【解决方案5】:

        它本质上是一个状态机,所以你可以用 std::regex 做一些聪明的事情,但这里有一个简单的实现。

        void TrimConsecutiveValues(int value, int cKeep, std::list<int> &list) {
          int cSeen = 0;
          auto it = list.begin();
          while (it != list.end()) {
            if (*it == value) {
              if (cSeen < cKeep) {
                ++cSeen;
                ++it;
              } else {
                it = list.erase(it);
              }
            } else {
              cSeen = 0;
              ++it;
            }
          }
        }
        

        【讨论】:

        • list::erase 返回一个迭代器,it = list.erase(it); 看起来好多了
        • old 来自哪里?
        • 来自我为响应 Csq 的评论而错误编辑的原始版本。再次修复。
        【解决方案6】:

        这是一个 C++11 版本(使用auto、lambdas 和移动语义),它使用std::vector 来表示T 类型的任意value

        #include <algorithm>
        #include <cstddef>
        #include <iostream>
        #include <iterator>
        #include <vector>
        
        template<std::size_t N, typename T>
        std::vector<T> collapse_consecutive(std::vector<T> v, T const& value)
        {
            if (v.size() <= N) return v;
        
            for (auto it = v.begin(); it != std::prev(v.end(), N); ++it) {
                if (*it == value) {
                    // find first following mismatch
                    auto jt = std::find_if(it, v.end(), [&](T const& elem) {               
                       return elem != value;
                    });
                    // keep first N matches, remove rest of matches
                    if (std::distance(std::next(it, N), jt) > 0)               
                        v.erase(std::remove(std::next(it, N), jt, value), jt);
                }
            }
            std::for_each(v.begin(), v.end(), [](int const& elem) { std::cout << elem << ", "; });
            std::cout << "\n";
            return v;
        }
        
        int main()
        {
           std::vector<int> v = {7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 0, 8};
        
          collapse_consecutive<0>(v, 0);
          collapse_consecutive<1>(v, 0);
          collapse_consecutive<2>(v, 0);
          collapse_consecutive<3>(v, 0);
          collapse_consecutive<4>(v, 0);
          collapse_consecutive<5>(v, 0);  
        }
        

        LiveWorkSpace 上的输出

        stdout: 
        7, 12, 2, 27, 10, 8, 
        7, 0, 12, 0, 2, 0, 27, 10, 0, 8, 
        7, 0, 12, 0, 0, 2, 0, 0, 27, 10, 0, 0, 8, 
        7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 8, 
        7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 0, 8, 
        7, 0, 12, 0, 0, 2, 0, 0, 0, 27, 10, 0, 0, 0, 0, 8, 
        

        【讨论】:

          猜你喜欢
          • 2012-07-28
          • 1970-01-01
          • 2019-12-09
          • 1970-01-01
          • 1970-01-01
          • 2014-04-01
          • 1970-01-01
          • 2015-03-05
          • 2021-06-03
          相关资源
          最近更新 更多