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

Ответ

Оптимальность зависит от требования к сохранению порядка элементов. Рассмотрим два основных подхода для массива типа int.

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

// arr - указатель на массив, size - ссылка на его текущий размер, index - индекс удаляемого элемента
void removeElementFast(int* arr, int& size, int index) {
    if (index < 0 || index >= size) return; // Проверка валидности индекса
    // Копируем последний элемент на место удаляемого
    arr[index] = arr[size - 1];
    size--; // "Удаляем" последний элемент, уменьшая размер
}

2. Удаление с сохранением порядка (O(n)): Если порядок элементов должен остаться неизменным, необходимо сдвинуть все элементы, следующие за удаляемым, на одну позицию влево.

void removeElementOrdered(int* arr, int& size, int index) {
    if (index < 0 || index >= size) return;
    // Сдвигаем элементы
    for (int i = index; i < size - 1; ++i) {
        arr[i] = arr[i + 1];
    }
    size--;
}

Выбор метода:

  • Использую быстрый метод (O(1)), когда структура данных представляет собой множество, и порядок не имеет значения.
  • Использую метод с сохранением порядка (O(n)), когда массив представляет собой упорядоченную последовательность (например, историю операций), и этот порядок критичен для логики приложения. В реальных проектах на C++ для таких задач обычно используется std::vector и его метод erase, который внутри выполняет подобный сдвиг.

Ответ 18+ 🔞

А, ну это же классика, ёпта! Сидишь ты такой, пишешь код, и тут бац — надо элемент из массива выкинуть. А как, блядь, сделать это оптимально? Тут, понимаешь, всё упирается в один простой вопрос: тебе порядок элементов важен или похуй?

1. Удаление по принципу «да похуй на порядок» (O(1), то есть мгновенно): Если тебе всё равно, как там элементы лежат, главное — чтобы нужного не было, то тут хитрая жопа пригодится. Берёшь последний элемент и хуяк его на место того, который удаляешь. Всё, делов-то! Размер уменьшил — и готово, нихуя не сдвигая.

// arr - указатель на массив, size - ссылка на его текущий размер, index - индекс удаляемого элемента
void removeElementFast(int* arr, int& size, int index) {
    if (index < 0 || index >= size) return; // Проверка валидности индекса
    // Копируем последний элемент на место удаляемого
    arr[index] = arr[size - 1];
    size--; // "Удаляем" последний элемент, уменьшая размер
}

2. Удаление с сохранением порядка (O(n), то есть придётся потрудиться): А вот если порядок священен и его трогать нельзя — например, это список транзакций или там очередь сообщений — тогда придётся впендюрить рутинную работу. Начинаешь сдвигать все элементы после удаляемого на одну позицию влево. Это, конечно, медленнее, особенно если массив — овердохуища большой.

void removeElementOrdered(int* arr, int& size, int index) {
    if (index < 0 || index >= size) return;
    // Сдвигаем элементы
    for (int i = index; i < size - 1; ++i) {
        arr[i] = arr[i + 1];
    }
    size--;
}

Так что же выбрать, чувак?

  • Быстрый метод (O(1)) — когда у тебя просто мешок с данными, типа множество уникальных айдишников. Порядок нихуя не важен, была бы скорость.
  • Метод с сохранением порядка (O(n)) — когда массив это, блядь, священная последовательность. В нормальной жизни для такого уже давно используют std::vector и его метод erase, который внутри делает ровно этот самый сдвиг, так что не парься и используй готовое, не изобретай велосипед.