【问题标题】:how to group same elements when iterating array (e.g.:{1,2,2,2,1,1,1,3,3,2,2})?迭代数组时如何对相同的元素进行分组(例如:{1,2,2,2,1,1,1,3,3,2,2})?
【发布时间】:2016-03-21 02:45:27
【问题描述】:

例如,我有数组 {1,2,2,2,1,1,1,3,3,2,2},我想对其进行迭代以打印类似的内容:

1 
2 2 2 
1 1 1 
3 3 
2 2

所以我创建了一个类似的程序(从真实代码简化):

int numArray[]={1,2,2,2,1,1,1,3,3,2,2}; //in real code it may be something like MyObject myObjectArray[]
vector<int> store;
for(int i=0;i<sizeof(numArray)/sizeof(int);i++){
    //in real case it may check store.back()!=myObject.getNum()
    if(store.size()>0 && store.back()!=numArray[i]){
        for(int j=0;j<store.size();j++){
            printf("%d ",store[j]);//(it may be someFunction(store[j]) in real cases)
        }
        printf("\n"); //(it may be changePattern() in real case)
        store.clear();
    }
    store.push_back(numArray[i]);
}
for(int j=0;j<store.size();j++){
    printf("%d ",store[j]);
}
printf("\n");

但是这个程序不是很好维护,首先,它需要额外的向量来复制和存储一些临时结果,还有这个代码:

for(int j=0;j<store.size();j++){
    printf("%d ",store[j]);
}
printf("\n");

需要同时出现在for循环的内部和外部。

是否可以有一些简单的 for 循环,例如:

for(int i=0;i<sizeof(numArray)/sizeof(int);i++){
    if(//something like numArray[i]!=numArray[i-1]){
        for(//start from last changed element to current element){
        }
    }
    printf("\n");
}

哪个不需要存储临时结果,不需要重复代码?

【问题讨论】:

  • 我已经看到了很多针对您的问题或情况的答案,其中一些非常好,有些甚至使用了 auto 关键字,另一些则使用了带有特定算法的 stl 库。我提供的答案可以追溯到基本编程。当您开始使用带有嵌套 if 循环和另一个嵌套 for 循环的 for 循环时,您可能希望使用 while 循环。查看我的答案,它应该为您提供一些非常好的见解。

标签: c++ arrays for-loop iteration


【解决方案1】:

我很惊讶到目前为止没有一个答案尝试使用标准库。

函数std::adjacent_find用于查找序列中连续的两个相同的项;我们想做相反的事情,即在一个序列中找到两个不同的项目。这就像将否定谓词(即std::not_equal_to)传递给adjacent_find 一样简单。

使用它,完整的解决方案如下所示:

constexpr int numArray[] = {1,2,2,2,1,1,1,3,3,2,2};

// Initialise the iterator
auto first = std::cbegin(numArray);

while (first != std::cend(numArray)) {
    // adjacent_find returns the *first* of each pair it finds....
    auto iter = std::adjacent_find(first, std::cend(numArray),
                                   std::not_equal_to<>{});
    // ...but we want the second member of each pair, as long as that's
    // not past the end of the array
    if (iter != std::cend(numArray)) {
        iter = std::next(iter);
    }

    // Print the array slice, followed by a newline
    std::copy(first, iter, std::ostream_iterator<int>{std::cout});
    std::cout << "\n";

    // Move on
    first = iter;
}

打印出来

1
222
111
33
22

根据需要

【讨论】:

  • 使用std::adjacent_find 是个好主意,没想到。
  • @Kupiakos 那么通常的做法是给予信任,而不是编辑你的答案来复制这个
  • 我不熟悉这里的礼仪。应该将功劳放在评论、编辑描述(我做了)还是答案本身?
【解决方案2】:

您无需以任何方式对数字进行分组即可获得所需的输出。每次发现numArray[i] != numArray[i-1]时,您只需打印'\n'

for (int i = 0 ; i != 11 ; i++) {
    if (i > 0 && numArray[i] != numArray[i-1]) {
        cout << endl;
    }
    cout << numArray[i] << " ";
}

Demo.

【讨论】:

  • 虽然这个问题非常好,但它并不能解决在迭代数组时查找连续元素组的一般问题。
  • 我在提供的答案中采用了不同的方法;我改用了一个while循环;我还解决了对相似的连续项目进行分组的问题。
  • @Kupiakos 当然可以:您只需将打印替换为将数字保存在集合中,将打印endl 替换为将集合保存在组列表中。
【解决方案3】:

使用迭代器和 lambda:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>

template <class Iterator, class Func, class BinaryPredicate>
void group(Iterator begin, Iterator end, Func callFunc, BinaryPredicate breakCondition)
{
    auto endgroup = begin;
    while (endgroup != end)
    {
        auto begingroup = endgroup;
        endgroup = std::adjacent_find(endgroup, end, breakCondition);
        if (endgroup != end)
            endgroup = std::next(endgroup);
        callFunc(begingroup, endgroup);
    }
}

template <class Iterator, class Func>
inline void group(Iterator begin, Iterator end, Func callFunc)
{
    ::group(begin, end, callFunc, std::not_equal_to<>{});
}

int main()
{
    int v[] = {1,2,2,2,1,1,1,3,3,2,2};
    ::group(std::begin(v), std::end(v),
        [](auto start, auto end){
            while (start != end)
                std::cout << *start++ << ((start == end) ? "" : " ");
            std::cout << std::endl;
        });
    return 0;
}

这具有使用任何可比较类型的任何前向迭代容器的优点,并且可以使用任何带有开始和结束迭代器的函数。该函数被赋予指向组的开始和结束的迭代器,如向量迭代器。然后它可以对组做任何它想做的事情,包括编辑。您还可以传递另一个分组中断条件,例如使用std::greater&lt;&gt;{} 仅对升序元素进行分组。

【讨论】:

  • std::adjacent_find@TristanBrindle 使用礼貌
  • 不错的可重用代码;完全不同于我的回答和方法。在我提供的答案中;我回到了简单循环和逻辑流的编程基础。
【解决方案4】:

我会这样做:

const size_t len = sizeof(numArray) / sizeof(int);
for (size_t start = 0; start < len; ) {
    for (size_t ii = start; ii < len && numArray[ii] == numArray[start]; ++ii) {
        printf("%d ", numArray[cur]);
    }
    printf("\n");
    start = ii;
}

【讨论】:

    【解决方案5】:
    int group_start = 0;
    for(int i=0;i<sizeof(numArray)/sizeof(int);i++){
        if (i > 0 && numArray[i] != numArray[i-1]) {
            printf("\n");
            group_start = i;
        }
        if (i != group_start) {
            printf(" ");
        }
        printf("%d", numArray[i]);
    }
    printf("\n");
    

    这可以解决您的玩具问题,尽管它可能无法准确解决真正的问题,具体取决于它是什么。无论如何,您绝对不需要存储临时项目列表;您只需要您正在制作的组中第一项的索引。在此示例中,可以随时打印项目,并且开始索引仅用于正确分隔内容(您的示例代码未能做到这一点,以一种较小的方式 - 它在换行符之前打印一个空格)。但是,如果您正在做一些更复杂的事情,需要在事后循环这些项目,group_starti 会为您提供您需要循环的范围。

    有一段代码需要复制,即printf("\n"),但这不是什么大问题。如果你有更复杂的东西想要避免重复,只需把它放在一个函数中,你唯一需要写两次的就是函数调用。

    【讨论】:

      【解决方案6】:

      这对我来说是个脑筋急转弯,所以我想出了这个伪代码来做这件事。不确定它有多正确,但它确实通过了一个测试用例:

      var input = [1,2,2,2,1,1,1,3,3,2,2]
      
      if (input.length >= 2) {
        for (var i = 0; i < input.length - 1; i++) {
          if (input[i] == input[i + 1]) {
            print(input[i] + ' ')
          } else {
            print(input[i] + '\n')
          }
        }
        print(input[input.length-1] + '\n')
      } else if (input.length == 1) {
        print(input[0] + '\n')
      }
      

      在 Javascript(JSFiddle)中:

      https://jsfiddle.net/efy1dr9x/3/

      【讨论】:

        【解决方案7】:
        int numArray[]={1,2,2,2,1,1,1,3,3,2,2};
        for(i=0; i<sizeof(numArray)/sizeof(int); i++) { 
            if(i==0)
                printf("%d ",numArray[i]);
            else if(numArray[i]!=numArray[i-1])     
                printf("\n%d ",numArray[i]);
            else  
                printf("%d ",numArray[i];
        }
        

        【讨论】:

          【解决方案8】:

          我认为您在考虑这一点时过于认真:除非您需要保留重复值或模式的记录,否则无需任何临时人员或复制或移动数据。您可以通过在现有容器中直接轻松地工作来实现这一点。此外,当您发现自己开始使用带有嵌套 if 语句和另一个嵌套 for 循环的 for 循环时;这时你应该用一个带有几个 if 语句的 while 循环替换整个代码块。有时最好回到基础并做简单的逻辑流程。试试这个,让我知道你的想法。

          #include <iostream>
          
          int main() {
              // Your Existing Array or Container
              int numArray[] = {1,2,2,2,1,1,1,3,3,2,2};
              // Number Of Elements in that container
              unsigned numElements = sizeof(numArray) / sizeof(int);
          
              // Initial Index Value For Our While Loop;
              int index = 0;
              // Loop From Initial Index To Last Element
              while ( index < numElements ) {
                  // Check To See If Index Is Last Element If So Break From Loop
                  if ( index == numElements ) {
                      break;
                  }
          
                  // First Do Not Check To See If The Value At Index & Next Are Equal, Rather Check
                  // To See If They Are Not Equal. If They Are Not, Then Print The Value At That
                  // Index And A New Line, Otherwise Just Print The Value At That Index And A Space.
                  if ( numArray[index] != numArray[index+1] ) {
                      std::cout << "numArray[index] << std::endl;
                  } else {
                      std::cout << numArray[index] << " ";
                  }
                  // Increment Our Counter
                  index++;
              }
          
              // We Are Now Done; Not Necessary To Create Temporaries Or To Copy Or Move Data
              // It Is A Little Easier To Read And Follow Logic Flow As Opposed To
              // Having For Loop -> Nested If Stated -> Nested For Loop & More...
              return 0;
          } 
          

          您需要做的就是遵循这个 while 循环的模式或流程,它工作得很好。循环遍历容器的大小,首先检查退出条件,然后检查满足哪些条件才能分支到您需要执行的逻辑操作过程,最后增加您的计数器。这里唯一的主要区别是我使用的是 while 循环而不是 for 循环,并且我使用的是 std::cout 而不是 printf()。

          现在,如果您需要对它们进行分组,可以将其添加到同一个 while 循环中。

          #include <iostream>
          #include <vector>
          
          int main() {
              int numArray[] = {1,2,2,2,1,1,1,3,3,2,2};
              unsigned numElements = sizeof(numArray) / sizeof (int);    
          
              std::vector<int> group;
              std::vector<std::vector<int>> groups;
          
              int index = 0;
              while ( index < numElements ) {
                  if ( index == numElements ) {
                      break;
                  }
                  if ( numArray[index] != numArray[index+1] ) {
                      std::cout << numArray[index] << std::endl;
                      group.push_back( numArray[index] ); // Add To Group
                      groups.push_back( group );   // Add To Groups
                      group.clear(); // Clear Group For Change In Value
                      //std::cout << "\n";
                  } else {
                      std::cout << numArray[index] << " ";
                      group.push_back( numArray[index] ); // Just Add To Group Do Not Clear           
                  }
                  index++;
              }
          
              std::cout << std::endl;
          
              // Test Groups;
              for ( unsigned i = 0; i < groups.size(); i++ ) {
                  for ( unsigned j = 0; j < groups[i].size(); j++ ) {
                      std::cout << groups[i][j] << " ";
                  }
                  std::cout << std::endl;
              }
          
              return 0;
          } // main
          

          现在,如果您想让它更加模块化、可重用和通用,您可以将其放入模板函数中:

          #include <iostream>
          #include <vector>
          
          template<typename T>
          void groupObjects( unsigned startIndex, unsigned size, T* objectArray, std::vector<std::vector<T>>& groups ) {
          
              std::vector<T> group;
          
              while ( startIndex < size ) {
                  if ( startIndex == size ) {
                      break;
                  }
          
                  if ( objectArray[startIndex] != objectArray[startIndex+1] ) {
                      std::cout << objectArray[startIndex] << std::endl;
                      group.push_back( objectArray[startIndex] );
                      groups.push_back( group );
                      group.clear();
                  } else {
                      std::cout << objectArray[startIndex] << " ";
                      group.push_back( objectArray[startIndex] );
                  }
                  startIndex++;
              }
          }
          
          int main() {    
              int numArray[] = {1,2,2,2,1,1,1,3,3,2,2};
              unsigned numElements = sizeof(numArray) / sizeof (int);
          
              std::vector<std::vector<int>> results;
              groupObjects<int>( 0, numElements, numArray, results );
          
              std::cout << std::endl;
          
              for ( unsigned i = 0; i < results.size(); i++ ) {
                  for ( unsigned j = 0; j < results[i].size(); j++ ){
                      std::cout << results[i][j] << " ";
                  }
                  std::cout << std::endl;
              }
          
              return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 2016-08-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-19
            • 2021-09-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多