Как удалить произвольный элемент вектора за константное время, если не важен порядок?

Ответ

Если порядок элементов в std::vector не важен, эффективный способ удаления элемента по индексу за O(1) — переместить последний элемент вектора на место удаляемого и вызвать pop_back().

Реализация:

#include <vector>
#include <utility> // для std::move

template <typename T>
void erase_unordered(std::vector<T>& v, typename std::vector<T>::size_type pos) {
    if (pos >= v.size()) return; // Выход за границы
    if (pos != v.size() - 1) {
        v[pos] = std::move(v.back()); // Перемещаем последний элемент
    }
    v.pop_back(); // Удаляем последний элемент (теперь это дубликат)
}

Как это работает и почему O(1):

  1. Вместо сдвига всех элементов после pos (что стоит O(n)), мы берём последний элемент.
  2. Перемещаем (или копируем) его на место удаляемого с помощью std::move.
  3. Удаляем последний элемент с помощью pop_back() (амортизированная константная сложность).

Критические нюансы:

  • Нарушение порядка: Элементы меняют местами.
  • Инвалидация итераторов: Инвалидируются итераторы и ссылки на последний элемент и на элемент в позиции pos.
  • Пример использования:
    std::vector<int> data = {10, 20, 30, 40, 50};
    erase_unordered(data, 1); // Удаляем элемент с индексом 1 (20)
    // data теперь может быть: {10, 50, 30, 40}
    // Порядок изменился: 50 встал на место 20.

    Это стандартный приём в game development и высокопроизводительных вычислениях, где важна скорость, а порядок не критичен.

Ответ 18+ 🔞

А, слушай, вот тебе реально годный лайфхак, если порядок элементов в векторе тебе похуй! Ну правда, зачем городить огород, если можно сделать всё за O(1) и не париться.

Представь себе: у тебя есть вектор, как очередь в столовой. И тебе нужно выкинуть какого-то челика из середины. Обычный vector::erase — это как если бы ты вытащил его, а потом заставил ВСЕХ, кто стоял за ним, сделать шаг вперёд. Ебать колотить, какая морока, особенно если очередь длинная! Сложность O(n), и это пиздец как неэффективно.

А теперь смотри, как делают крутые пацаны в геймдеве и всяких хайперформанс-штуках:

#include <vector>
#include <utility> // для std::move

template <typename T>
void erase_unordered(std::vector<T>& v, typename std::vector<T>::size_type pos) {
    if (pos >= v.size()) return; // Чтоб не выстрелить себе в ногу
    if (pos != v.size() - 1) {
        v[pos] = std::move(v.back()); // Хоба! Последнего — на место удаляемого!
    }
    v.pop_back(); // И просто отрезаем хвост
}

Суть в чём, ёпта?

  1. Вместо того чтобы сдвигать овердохуища элементов, мы просто берём последний элемент вектора.
  2. Берём его и перемещаем (через std::move, это важно!) на место того, кого мы хотим удалить. Это как если бы ты в той очереди из столовой просто позвал последнего человека и сказал: «Слушай, встань-ка ты вот сюда, на место этого чувака, которого мы выгоняем».
  3. А потом просто отрезаешь теперь уже ненужный последний элемент через pop_back(). Всё, операция завершена!

Но, бля, есть нюансы, о которых кричать надо:

  • Порядок ебётся полностью. После такой операции элементы будут не в том порядке, в котором ты их засунул. Последний встал в середину. Если порядок важен — это не твой метод, иди нахуй со своим O(1).
  • Итераторы летят в пизду. Все итераторы и ссылки, которые указывали на последний элемент И на тот элемент, который ты «удалил», теперь инвалидированы. Доверия к ним — ебать ноль.
  • Как это выглядит на практике:
    std::vector<int> data = {10, 20, 30, 40, 50};
    erase_unordered(data, 1); // Удаляем 20 (индекс 1)
    // Теперь data может быть таким: {10, 50, 30, 40}
    // Видишь? 50 встал на место 20. Порядок изменился, но вектор стал короче, и сделано это было моментально.

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