【问题标题】:How to count equal, adjacent elements in a vector?如何计算向量中相等的相邻元素?
【发布时间】:2016-09-20 14:06:47
【问题描述】:

假设我有一个vector<int> { 1, 1, 2, 3, 3, 3, 1, 1 },我想将其转换为“相邻元素计数”的vector<std::pair<int, int>> { {1, 2}, {2, 1}, {3, 3}, {1, 2} }

我可能会用一个指示新“邻接集”开始的标志和一个计算连续元素数量的计数器来迭代向量。我只是想知道 STL 中是否还没有更抽象和优雅的解决方案,因为这似乎是一个非常常见的用例。像 unique、similar_find 或 equal_range 这样的算法似乎非常接近我正在寻找的东西,但不是很正确,而且我自己从头开始实施它可能没有任何好处。

【问题讨论】:

  • "我只是想知道 STL 中是否还没有更抽象和优雅的解决方案,因为这似乎是一个非常常见的用例" 对于向量?也许对于一张地图来说,仍然“常见”是一个延伸。
  • C++ 库中没有内置算法可以做这样的事情。由你来实现它。听起来你已经很好地掌握了算法,所以等待一个不会来自中间管的人的答案是没有意义的。
  • 向量中的相邻元素是什么?至少有两种解释。是什么阻碍了您简单地通过向量进行交互?
  • @AmiTavory 正确,我这边的错字。对不起。

标签: c++ algorithm vector stl


【解决方案1】:

从算法的角度来看,我想说的是run-length encoding。我认为没有现成的算法可以做到这一点,但代码应该是微不足道的:

std::vector<std::pair<int, int>> out; 
for (int i: in)
{
     if (out.empty() || out.back().first != i)
     {
         out.emplace_back(i, 1);
     }
     else
     {
         ++out.back().second;
     }
}

Live-example.

【讨论】:

  • 我觉得这个实现非常优雅。
【解决方案2】:

Eric Niebler's range library,其中AFAIU is in the process of becoming part of the standard library 有一个与Python's itertools.groupby 非常相似的group_by,并且可以根据需要对连续的等效元素进行分组。

要对向量进行分组,您可以从

const vector<int> l{ 1, 1, 2, 3, 3, 3, 1, 1 };
auto x = l | view::group_by(std::equal_to<int>());

这意味着x是一个视图,其中相邻整数属于一个组,如果整数相等。

现在要迭代并打印每个连续的组及其大小,您可以执行以下操作(我相信您可以做得比以下更好,但这是我使用此库的限制):

for (auto i = x.begin();i != x.end(); ++i) 
    cout <<  *((*i).begin()) << " " << to_vector(*i).size() << endl;

示例

#include <vector>
#include <iostream>
#include <range/v3/all.hpp>

int main(int argc, char **argv) { 
    const std::vector<int> l{ 1, 1, 2, 3, 3, 3, 1, 1 };
    auto x = l | ranges::view::group_by(std::equal_to<int>());

    for (auto i = x.begin();i != x.end(); ++i) 
        std::cout <<  *((*i).begin()) << " " << ranges::to_vector(*i).size() << std::endl;
}

打印出来

$ ./a.out 
1 2
2 1
3 3
1 2

【讨论】:

    【解决方案3】:

    据我所知,没有这样的 C++ 库可以自动执行您的要求。
    无论如何,实现这一点非常简单。这是一种方法:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    void count_equal_elements(vector<int>& vec, vector<pair<int,int> >& result){
        if (vec.empty())
            return;
        int curr = vec[0];
        int count = 1;
        for (vector<int>::iterator it = vec.begin()+1; it != vec.end(); ++it){
            if (curr == *it){
                count++;
            }
            else{
                result.push_back(make_pair(curr,count));
                curr = *it;
                count = 1;
            }
        }
        result.push_back(make_pair(curr,count));
    }
    

    查看ideone

    【讨论】:

      【解决方案4】:

      使用std,您可以这样做:

      template <typename T>
      std::vector<std::pair<T, std::size_t>>
      adjacent_count(const std::vector<T>& v)
      {
          std::vector<std::pair<T, std::size_t>> res;
      
          for (auto it = v.begin(), e = v.end(); it != e; /*Empty*/) {
              auto it2 = std::adjacent_find(it, e, std::not_equal_to<>{});
              if (it2 != e) {
                  ++it2;
              }
              res.emplace_back(*it, std::distance(it, it2));
              it = it2;
          }
          return res;
      }
      

      Demo

      template <typename T>
      std::vector<std::pair<T, std::size_t>>
      adjacent_count(const std::vector<T>& v)
      {
          std::vector<std::pair<T, std::size_t>> res;
      
          for (auto it = v.begin(), e = v.end(); it != e; /*Empty*/) {
              const auto it2 = std::find_if(it, e, [&](const auto&x) { return x != *it; });
              res.emplace_back(*it, std::distance(it, it2));
              it = it2;
          }
          return res;
      }
      

      Demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多