В приведённом фрагменте Python:
def remove_dups(arr):
for i in range(len(arr)):
if arr[i] in arr[:i]:
arr.pop(i)
return arr
объясните, что делает код, найдите ошибки (логические и по сложности), предложите корректную и эффективную реализацию удаления дубликатов с сохранением порядка

25 Сен в 13:28
23 +1
0
Ответы
1

Кратко о том, что делает исходный код

def remove_dupsarrarrarr:
for i in rangelen(arr)len(arr)len(arr):
if arriii in arr:i:i:i:
arr.popiii return arr

Автор пытается пройти по массиву по индексам и для каждого элемента удалить его, если он уже встречался слева вarr[:i]в arr[:i]вarr[:i].arr:i:i:i — срез, содержащий все элементы слева от позиции i, проверка arriii in arr:i:i:i — ищет дубликат слева.Если найден дубликат — выполняется arr.popiii удалениепоиндексуудаление по индексуудалениепоиндексу.

Ошибки и недостатки

Логическая/ошибка исполнения:

Нельзя безопасно изменять длину списка poppoppop при итерации по rangelen(arr)len(arr)len(arr), потому что range вычислен заранее. После уменьшения длины списка обращение к arriii при дальнейшем i может вызвать IndexError. Пример:
arr = 1,1,21,1,21,1,2 i=0: оставить
i=1: удалить второй 1 → arr = 1,21,21,2 i=2: попытка обратиться к arr222 → IndexError.

Кроме того, удаление по индексу сдвигает оставшиеся элементы, что может привести к пропуску проверки некоторых элементов авторскийподходненадёжныйавторский подход ненадёжныйавторскийподходненадёжный.

Сложность эффективностьэффективностьэффективность:

Для каждого i создаётся срез arr:i:i:i — это Oiii по времени и дополнительная память; затем проверка in — ещё Oiii в худшем случае. Так что одна проверка ≈ Oiii, суммарно On2n^2n2.popiii удаление в середине списка — Onnn сдвиг элементов; в худшем случае суммарно ещё On2n^2n2.Итого алгоритм работает за квадратичное время ииспользуетдополнительнуюпамятьиз−засрезови использует дополнительную память из-за срезовииспользуетдополнительнуюпамятьиззасрезов. Для больших списков это плохо.

Корректная и эффективная реализация ссохранениемпорядкас сохранением порядкассохранениемпорядка

1) Простая и понятная возвращаетновыйсписок,ожидается,чтоэлементыхешируемывозвращает новый список, ожидается, что элементы хешируемывозвращаетновыйсписок,ожидается,чтоэлементыхешируемы:
def remove_dupsarrarrarr:
seen = set res = for x in arr:
if x not in seen:
seen.addxxx res.appendxxx return res

Сложность: Onnn в среднем по времени, Onnn по дополнительной памяти.

2) Вариант, модифицирующий список in-place безсозданияновогосписка,носдополнительныммножествомдляseenбез создания нового списка, но с дополнительным множеством для seenбезсозданияновогосписка,носдополнительныммножествомдляseen:
def remove_dups_inplacearrarrarr:
seen = set write = 0
for x in arr:
if x not in seen:
seen.addxxx arrwritewritewrite = x
write += 1
del arrwrite:write:write: # обрезаем хвост
return arr

Этот вариант экономит место на новый список, но всё равно использует Onnn для множества seen.

3) Если элементы не хешируемы, можно использовать множество ключей keykeykey или fallback на проверку по списку медленнеемедленнеемедленнее:
def remove_dups_keyarr,key=lambdax:xarr, key=lambda x: xarr,key=lambdax:x:
seen = set res = for x in arr:
k = keyxxx if k not in seen:
seen.addkkk res.appendxxx return res

Примеры и замечания

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

Если хотите, могу показать конкретные тесты, демонстрирующие падение исходного кода и корректность предложенных реализаций.

25 Сен в 14:03
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир