Нахождение пробелов в последовательности чисел - PullRequest
6 голосов
/ 19 декабря 2008

У меня есть std :: vector, содержащий несколько чисел, которые не в каком-либо определенном порядке, и могут иметь или не иметь пробелы между числами - например, у меня может быть {1,2,3, 6} или {2,8,4,6} или {1, 9, 5, 2} и т. д.

Я бы хотел простой способ взглянуть на этот вектор и сказать: «дайте мне наименьшее число> = 1, которое не появляется в векторе». Итак,

для трех приведенных выше примеров ответы будут 4, 1 и 3 соответственно.

Это не критично для производительности, и список короткий, поэтому нет никаких проблем с копированием списка и его сортировкой, например.

На самом деле я не застрял в способе сделать это, но мои навыки STL серьезно атрофированы, и я чувствую, что собираюсь сделать что-то нехорошее - мне было бы интересно посмотреть, что придут другие люди. 1011 *

Ответы [ 9 ]

12 голосов
/ 19 декабря 2008

Стандартный алгоритм, который вы ищете, это std ::acent_find .

Вот решение, которое также использует лямбду для очистки предиката:

int first_gap( std::vector<int> vec )
{
  // Handle the special case of an empty vector.  Return 1.
  if( vec.empty() )
    return 1;

  // Sort the vector
  std::sort( vec.begin(), vec.end() );

  // Find the first adjacent pair that differ by more than 1.
  auto i = std::adjacent_find( vec.begin(), vec.end(), [](int l, int r){return l+1<r;} );

  // Handle the special case of no gaps.  Return the last value + 1.
  if ( i == vec.end() )
    --i;

  return 1 + *i;
}
10 голосов
/ 19 декабря 2008

Проверенный ответ использует <для сравнения. ! = намного проще: </p>

int find_gap(std::vector<int> vec) {
    std::sort(vec.begin(), vec.end());
    int next = 1;
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
        if (*it != next) return next;
       ++next;
    }
    return next;
}

find_gap(1,2,4,5) = 3
find_gap(2) = 1
find_gap(1,2,3) = 4

Я не передаю ссылку на вектор, поскольку а) он сказал, что время не имеет значения и б) поэтому я не меняю порядок исходного вектора.

4 голосов
/ 19 декабря 2008

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

Это должно быть асимптотически быстрее, чем сортировка и поиск. Он будет использовать больше памяти, чем сортировку, если вам разрешено уничтожать оригинал, но меньше памяти, чем сортировку, если вам нужно сохранить оригинал.

4 голосов
/ 19 декабря 2008

Сортировка списка, а затем выполнение линейного поиска кажется самым простым решением. В зависимости от ожидаемого состава списков, вы можете использовать алгоритм сортировки менее общего назначения, и если вы реализуете сортировку самостоятельно, вы можете отслеживать данные во время сортировки, которые можно использовать для ускорения (или полного исключения) этапа поиска. Я не думаю, что есть какое-то особенно элегантное решение этой проблемы

2 голосов
/ 19 декабря 2008

На самом деле, если вы сделаете сортировку по пузырькам (вы знаете ... ту, которой вас учат сначала, а потом скажут никогда больше не использовать ...), вы сможете обнаружить первый пробел на ранней стадии сортировки. процесс, так что вы можете остановиться там. Это должно дать вам самое быстрое общее время.

2 голосов
/ 19 декабря 2008

Sort-н-поиск:

std::sort(vec.begin(), vec.end());
int lowest = 1;
for(size_t ii = 1; ii < vec.size(); ++ii)
{
    if (vec[ii - 1] + 1 < vec[ii])
    {
        lowest = (vec[ii - 1] + 1);
        break;
    }
}

/* 1, 2, ..., N case */
if (lowest == vec[0]) lowest = (*vec.back()) + 1;

Итераторы могут использоваться с таким же ясным намерением, как и , продемонстрированное в ответе @ joe_mucchiello ( ed: лучше ) .

1 голос
/ 19 декабря 2008

вы могли бы пойти с чем-то вроде ....

struct InSequence
{
    int _current;   bool insequence;
    InSequence() : _current(1), insequence(true){}
    bool operator()(int x) {         
        insequence = insequence ? (x == _current) : false;  
        _current++; 
        return insequence;
    }
};

int first_not_in_sequence(std::vector<int>& v)
{
    std::sort(v.begin(), v.end());
    return 1+std::count_if(v.begin(), v.end(),InSequence());
}
1 голос
/ 19 декабря 2008

ОК, вот мои 2 цента. Предположим, у вас есть вектор длины N.

  1. Если N <= 2, вы можете проверить напрямую </li>
  2. Во-первых, используйте min_element, чтобы получить наименьший элемент, запомните его как emin
  3. Вызовите nth_element, чтобы получить элемент в N / 2, назовите его ehalf
  4. Если ehalf! = Emin + N / 2 слева есть пробел, примените этот метод рекурсивно, вызвав nth_element для всего массива, но запросив элемент N / 4. В противном случае, выполните рекурсию справа, запрашивая элемент 3 * N / 4.

Это должно быть немного лучше, чем полная сортировка.

0 голосов
/ 04 мая 2016

Возможная реализация ответа Томаса Каммейера

Я нашел подход Томаса действительно умным и полезным - поскольку некоторые из нас мечтают о коде, и я нахожу фактическую реализацию немного хитрой, я хотел предоставить некоторый готовый к использованию код.

Представленное здесь решение является как можно более общим:

  • Не делается никаких предположений относительно типа контейнера или диапазона, за исключением того, что их итераторы должны соответствовать требованиям ValueSwappable и RandomAccessIterator (из-за частичной сортировки с nth_element)
  • Можно использовать любой тип чисел - требуемые черты указаны ниже

Другое улучшение, которое я считаю, заключается в том, что условие отсутствия пробела можно проверить на ранней стадии: так как мы должны сканировать минимум в любом случае, мы также можем одновременно сканировать максимум и затем определять, содержит ли диапазон чисел даже пробел Стоит найти.

Последний, но не менее важный рекурсивный подход может быть адаптирован для отсортированных диапазонов! Если вы закодируете в параметре значения шаблона, был ли диапазон уже отсортирован, вы можете просто пропустить частичную сортировку и запретить определение минимального / максимального элементов.

#include <type_traits>
#include <iterator>
#include <tuple>
#include <utility>
#include <algorithm>
#include <cstddef>

// number type must be:
// * arithmetic
//   * subtractable (a - b)
//   * divisible by 2 (a / 2)
// * incrementable (++a)
// * less-than-comparable (a < b)
// * default-constructible (A{})
// * copy-constructible
// * value-constructible (A(n))
// * unsigned or number range must only contain values >0

/** Find lowest gap value in a range */
template<typename Range>
typename std::remove_reference_t<Range>::value_type
lowest_gap_value_unsorted(Range&& r)
{
    static_assert(!std::is_lvalue_reference_v<Range> && !std::is_const_v<Range>, "lowest_gap_value_unsorted requires a modifiable copy of the passed range");

    return lowest_gap_value_unsorted(std::begin(r), std::end(r), std::size(r));
}

/** Find lowest gap value in a range with specified size */
template<typename Range>
typename std::remove_reference_t<Range>::value_type
lowest_gap_value_unsorted(Range&& r, std::size_t N)
{
    static_assert(!std::is_lvalue_reference_v<Range> && !std::is_const_v<Range>, "lowest_gap_value_unsorted requires a modifiable copy of the passed range");

    return lowest_gap_value_unsorted(std::begin(r), std::end(r), N);
}

/** Find lowest gap value in an iterator range */
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
lowest_gap_value_unsorted(Iterator first, Iterator last)
{
    return lowest_gap_value_unsorted(first, last, std::distance(first, last));
}

/** Find lowest gap value in an iterator range with specified size */

template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
lowest_gap_value(Iterator first, Iterator last, std::size_t N)
{
    typedef typename std::iterator_traits<Iterator>::value_type Number;

    if (bool empty = last == first)
        return increment(Number{});

    Iterator minElem, maxElem;
    std::tie(minElem, maxElem) = std::minmax_element(first, last);

    if (bool contains0 = !(Number{} < *minElem))
        throw std::logic_error("Number range must not contain 0");

    if (bool missing1st = increment(Number{}) < *minElem)
        return increment(Number{});

    if (bool containsNoGap = !(Number(N) < increment(*maxElem - *minElem)))
        return increment(*maxElem);

    return lowest_gap_value_unsorted_recursive(first, last, N, *minElem);
}

template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
lowest_gap_value_unsorted_recursive(Iterator first, Iterator last, std::size_t N, typename std::iterator_traits<Iterator>::value_type minValue)
{
    typedef typename std::iterator_traits<Iterator>::value_type Number;

    if (N == 1)
        return ++minValue;
    if (N == 2)
    {
        // determine greater of the 2 remaining elements
        Number maxValue = !(minValue < *first) ? *std::next(first) : *first;
        if (bool gap = ++minValue < maxValue)
            return minValue;
        else
            return ++maxValue;
    }

    Iterator medianElem = std::next(first, N / 2);
    // sort partially
    std::nth_element(first, medianElem, last);

    if (bool gapInLowerHalf = (Number(N) / 2 < *medianElem - minValue))
        return lowest_gap_value_unsorted_recursive(first, medianElem, N / 2, minValue);
    else
        return lowest_gap_value_unsorted_recursive(medianElem, last, N / 2 + N % 2, *medianElem);
};

template<typename T>
T increment(T v)
{
    return ++v;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...