Разбираем сдвиги ноликов 0️⃣

В комментах вы представили чуть ли не все способы решения задачи, какие можно придумать.

Конечно, классический паттерн для решения тут — два указателя.
Вот идеальное решение со сложностью O(n) по времени и O(1) по памяти:

def move_zeroes(nums: list[int]) -> None:
insert_pos = 0

for i in range(len(nums)):
if nums[i] != 0:
# Свапаем ненулевой элемент с "левым" нулем
nums[insert_pos], nums[i] = nums[i], nums[insert_pos]
insert_pos += 1


Никаких аллокаций. Никаких pop() из середины списка. Проходим по массиву ровно один раз. Переменная insert_pos всегда указывает на первый доступный ноль. Как только находим число отличное от нуля — просто меняем их местами благодаря встроенному механизму распаковки кортежей в Python.

🐍 Но было бы не интересно без чего-то более хитрого. Вот вам питонячий флекс в одну строку без генераторов:
nums.sort(key=bool, reverse=True)


Как это работает? bool(0) дает False, остальные числа — True. Встроенная сортировка Timsort в Python стабильна — она строго сохраняет исходный порядок равных элементов. Все True (числа) съедутся влево, сохраняя свой порядок, а все False (нули) улетят вправо.

Да, формально тут сложность O(n log n), что алгоритмически хуже двух указателей. Но из-за того, что Timsort написан на зубодробительном C, на реальных списках небольшого объема этот код физически порвет циклы на чистом Python по скорости выполнения.

#алгособес