← Назад к вопросам
Какая алгоритмическая сложность удаления объекта в списке в 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)?
- Сначала ищет элемент (O(n) линейный поиск)
- Потом удаляет и сдвигает (O(n))
- Итого: 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 list | O(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) для удаления по значению