C++ получить индекс элемента массива по значению

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

есть ли более быстрый способ сделать это на C++? Структура STL, которую я использую для хранения массива, на самом деле не имеет для меня значения (это не должен быть вектор). Мой массив также уникален (без повторяющихся элементов) и упорядочен (например, список дат, идущих вперед во времени).

2 ответов


поскольку элементы отсортированы, вы можете использовать двоичный поиск, чтобы найти соответствующий элемент. Стандартная библиотека C++ имеет std::lower_bound алгоритм, который может быть использован для этой цели. Я бы рекомендовал обернуть его в свой собственный алгоритм бинарного поиска, для ясности и простоты:

/// Performs a binary search for an element
///
/// The range `[first, last)` must be ordered via `comparer`.  If `value` is
/// found in the range, an iterator to the first element comparing equal to
/// `value` will be returned; if `value` is not found in the range, `last` is
/// returned.
template <typename RandomAccessIterator, typename Value, typename Comparer>
auto binary_search(RandomAccessIterator const  first,
                   RandomAccessIterator const  last,
                   Value                const& value,
                   Comparer                    comparer) -> RandomAccessIterator
{
    RandomAccessIterator it(std::lower_bound(first, last, value, comparer));
    if (it == last || comparer(*it, value) || comparer(value, *it))
        return last;

    return it;
}

(стандартная библиотека C++ имеет std::binary_search, но он возвращает bool: true если диапазон содержит элемент false иначе. Это не полезно, если вы хотите, чтобы итератор элемент.)

как только у вас есть итератор для элемента, вы можете использовать std::distance алгоритм вычисления индекса элемента в диапазоне.

оба этих алгоритма одинаково хорошо работают с любой последовательностью случайного доступа, включая оба std::vector и обычные массивы.


если вы хотите связать значение с индексом и быстро найти индекс, вы можете использовать std::map или std::unordered_map. Вы также можете комбинировать их с другими структурами данных (например,std::list или std::vector) в зависимости от других операций, которые вы хотите выполнять на данных.

например, при создании вектора мы также создаем таблицу подстановки:

vector<int> test(test_size);
unordered_map<int, size_t> lookup;
int value = 0;
for(size_t index = 0; index < test_size; ++index)
{
    test[index] = value;
    lookup[value] = index;
    value += rand()%100+1;
}

теперь, чтобы посмотреть индекс вы просто:

size_t index = lookup[find_value];

использование структуры данных на основе хэш-таблицы (например, unordered_map) - довольно классический компромисс пространства/времени и может превзойти двоичный поиск для такого рода операции "обратного" поиска, когда вам нужно сделать много поисков. Другим преимуществом является то, что он также работает, когда вектор несортированный.

для удовольствия : -) я сделал быстрый тест в VS2012RC, сравнивая двоичный код поиска Джеймса с линейным поиском и с использованием unordered_map для поиска, все на векторе: Performance of various find index methods

до ~50000 элементов данная значительно (х3-4) outpeforms двоичный поиск, который демонстрирует ожидаемый o(зарегистрируйте N) поведение, несколько неожиданного результата заключается в том, что unordered_map теряет Это O(1) поведение последние 10000 элементов, предположительно из-за хеширования, возможно реализация вопроса.

EDIT: max_load_factor () для неупорядоченной карты равен 1, поэтому столкновений не должно быть. Разница в производительности между двоичным поиском и хэш-таблицей для очень больших векторов, по-видимому, заключается в кэшировании связанный и варьируется в зависимости от шаблона поиска в бенчмарке.

выбор между std:: map и std:: unordered_map говорит о разнице между упорядоченными и неупорядоченными картами.