← Назад к вопросам

Какая алгоритмическая сложность удаления объекта в списке в Python?

2.0 Middle🔥 61 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Какая алгоритмическая сложность удаления объекта в списке в Python?

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

Основные операции удаления

1. Удаление по индексу: list.pop(index) или del list[index]

Сложность: O(n) в среднем и худшем случаях

# Операция сдвигает все элементы после удаляемого
my_list = [1, 2, 3, 4, 5]
del my_list[2]  # Удаляем элемент с индексом 2 (значение 3)

# Что происходит в памяти:
# ДО:  [1, 2, 3, 4, 5]  ← индексы: 0, 1, 2, 3, 4
# ПОСЛЕ: [1, 2, 4, 5]    ← сдвиг элементов

Почему O(n)? Удаление требует сдвига всех элементов справа:

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Удаление с конца — быстро O(1)
my_list.pop()  # O(1) — просто удаляем последний, сдвигов нет

# Удаление с начала — медленно O(n)
my_list.pop(0)  # O(n) — нужно сдвинуть все 9 элементов!

# Удаление из середины — O(n/2) ≈ O(n)
my_list.pop(5)  # O(n) — нужно сдвинуть ~5 элементов

2. Удаление по значению: list.remove(value)

Сложность: O(n) в среднем случае

my_list = [1, 2, 3, 4, 5, 3, 6, 3]
my_list.remove(3)  # Удаляет первое вхождение
# my_list = [1, 2, 4, 5, 3, 6, 3]

Почему O(n)?

  1. Сначала ищет элемент (O(n) линейный поиск)
  2. Потом удаляет и сдвигает (O(n))
  3. Итого: O(n) + O(n) = O(n)
# В худшем случае элемента нет — просходит полный скан:
try:
    my_list.remove(999)  # O(n) — проходит весь список
except ValueError:
    print("Элемент не найден")

3. Удаление среза: del list[start:end]

Сложность: O(n) где n — количество элементов после среза

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Удаляем элементы с индексов 2 по 5
del my_list[2:5]  # [1, 2, 6, 7, 8, 9, 10]

# О(5) — нужно сдвинуть 5 элементов (6, 7, 8, 9, 10)

Таблица сложностей операций списка

ОперацияСложностьПример
list[i]O(1)Доступ по индексу
list.append(x)O(1) amortizedДобавить в конец
list.insert(i, x)O(n)Вставить в позицию
list.pop()O(1)Удалить с конца
list.pop(i)O(n)Удалить с позиции i
list.pop(0)O(n)Удалить с начала
list.remove(x)O(n)Удалить по значению
del list[i]O(n)То же, что pop(i)
del list[i:j]O(n)Удалить срез
x in listO(n)Поиск элемента

Демонстрация производительности

import time

def benchmark_pop_operations():
    for position_percent in [0, 50, 100]:  # начало, середина, конец
        my_list = list(range(1000000))
        position = (position_percent // 100) * len(my_list)
        
        start = time.time()
        for _ in range(1000):
            my_list.pop(position)
            my_list.insert(position, 0)  # восстанавливаем список
        elapsed = time.time() - start
        
        print(f"pop({position_percent}%): {elapsed:.4f}s")

# Результаты примерно:
# pop(0%): 0.8500s  — медленно (удаление с начала)
# pop(50%): 0.4200s — быстрее (удаление из середины)
# pop(100%): 0.0050s — очень быстро (удаление с конца)

Как оптимизировать

Проблема: Удаление нескольких элементов

# ❌ Неэффективно — O(n²)
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for item in [2, 4, 6]:
    my_list.remove(item)  # Каждый remove() — O(n)

# ✅ Лучше — O(n)
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
to_remove = {2, 4, 6}
my_list = [x for x in my_list if x not in to_remove]  # Одна итерация

# Или с set для O(1) поиска:
my_list = [x for x in my_list if x not in to_remove]  # O(n)

Проблема: Удаление из начала часто

# ❌ Неэффективно для удаления с начала
queue = []
for i in range(10000):
    queue.append(i)
    queue.pop(0)  # O(n) операция!

# ✅ Используй deque — O(1)
from collections import deque
queue = deque()
for i in range(10000):
    queue.append(i)
    queue.popleft()  # O(1) вместо O(n)!

Выбор структуры данных по операциям

# Если часто удаляешь с начала/конца:
from collections import deque
q = deque()  # O(1) для pop/popleft

# Если часто ищешь и удаляешь по значению:
data = set()  # O(1) для удаления (но нет порядка)
data.discard(value)  # O(1), не вызывает ошибку если нету

# Если нужен порядок и O(log n) удаление:
import bisect
data = []  # Отсортированный список
index = bisect.bisect_left(data, value)
if index < len(data) and data[index] == value:
    data.pop(index)  # Всё ещё O(n), но может быть индекс меньше

# Если O(1) удаление и поиск — используй специальные структуры:
# - Linked list (если пишешь сам)
# - SortedList из sortedcontainers
# - Heap (если нужна только минимум)

Резюме

  • list.pop(index) имеет сложность O(n) из-за необходимости сдвига элементов
  • list.pop() (конец) имеет O(1) — нет сдвигов
  • list.pop(0) (начало) имеет O(n) — максимальный сдвиг
  • list.remove(value) имеет O(n) — поиск + удаление
  • Для очереди используй deque — O(1) для операций с обеих сторон
  • Для множеств используй set — O(1) для удаления по значению
Какая алгоритмическая сложность удаления объекта в списке в Python? | PrepBro