C++ проверьте, сколько одинаковых элементов в строке в векторе

у меня есть большой вектор с 24.000 элементов, как :

(1,1,1,1,3,3,3,3,3,3,5,5,5,...etc)

и я хочу проверить, сколько же элементов в ряд: 4-6-3..так далее Я использую этот код :

static int counter=1;
vector<int>numbers;

for(int n=0;n<numbers.size()-1;n++)
{
  if(numbers[n]==numbers[n+1])
  {
    counter++;
  }
  else if(numbers[n]!=numbers[n+1])
  {
   cout<<counter<<endl;
   counter=1;
  }
}

есть ли алгоритм, который делает то же самое быстрее;

3 ответов


@rhalbersma в основном дал вам правильный ответ. В качестве дополнения, если вы хотите переписать свой алгоритм более стандартным образом:

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

int main()
{
    std::vector<int> v { 1, 1, 2, 3, 3, 5, 5, 5 }; // or whatever...

    auto i = begin(v);
    while (i != end(v))
    {
        auto j = adjacent_find(i, end(v), std::not_equal_to<int>());
        if (j == end(v)) { std::cout << distance(i, j); break; }
        std::cout << distance(i, j) + 1 << std::endl;
        i = next(j);
    }
}

здесь видео.

кроме того, когда вектор отсортирован, это даст вам лучшую сложность в лучшем случае:

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

int main()
{
    std::vector<int> v { 1, 1, 2, 3, 3, 5, 5, 5 }; // must be sorted...

    auto i = begin(v);
    while (i != end(v))
    {
        auto ub = upper_bound(i, end(v), *i);
        std::cout << distance(i, ub) << std::endl;
        i = ub;
    }
}

здесь видео.


Ваш алгоритм O(N) вовремя, и это кажется мне довольно оптимальным, так как вы должны посетить каждый уникальный элемент для сравнения. Вы все еще можете сбрить несколько циклов здесь и там, например,устранение условия внутри else() или включив некоторые настройки компилятора, но алгоритмически вы находитесь в хорошей форме.

если вход был уже отсортированный, вы могли бы сделать серия бинарных поисков. Это дало бы тебе O(N lg N) наихудшая сложность, но средний случай может быть значительно ниже в зависимости от средней длины равных последовательностей элементов.

кстати, как показывает @AndyProwl в своем ответе: стандартная библиотека очень высокий делать даже такие низкоуровневые алгоритмические вещи. The adjacent_find и upper_bound алгоритмы имеют хорошо документированные сложности, и соглашения итератора защитят вас для крайних случаев, которые присутствуют в вашем собственном коде. Как только вы узнаете это словарный запас, вы можете легко использовать их в своих собственных подпрограммах (и когда диапазоны приходят на C++, надеюсь, будет также легче их составить).


существует некоторая оптимизация litle, которая может дать вам несколько ms:

int size = numbers.size()-1;
static int counter=1;
static int *num1 = &numbers[0];
static int *num2 = &numbers[1];
for(int n=0;n<size;n++)
{
  if(*num1==*num2) counter++;
  else
  {
   cout << counter << "\n";
   counter=1;
  }
  num1++;
  num2++;
}
cout<<counter<<endl; //Caution, this line is missing in your code!!

Basicaly:

  • избегайте доступа к вектору по id: числа[n] означают, что компьютер должен умножить n*sizeof(int) и добавить этот результат к числам. Быстрее использовать указатель и увеличивать его, что означает просто добавление.