Программа курса:
Разбор задачи: Удаление дубликатов из отсортированного массива
Предложенный нами код решения:
def remove_duplicates(nums):
# Указатель для уникальных элементов
if not nums:
return 0
unique_index = 0
for i in range(1, len(nums)):
if nums[i] != nums[unique_index]:
unique_index += 1
nums[unique_index] = nums[i]
return unique_index + 1
Разбор:
Проверка на пустой список:
if not nums: return 0
Здесь проверяется, является ли список пустым. Если список пустой (
nums
равно[]
), функция немедленно возвращает0
, потому что в пустом списке нет уникальных элементов.Инициализация указателя для уникальных элементов:
unique_index = 0
Переменная
unique_index
будет использоваться для хранения индекса, на котором в массиве должны быть записаны уникальные элементы. Изначально этот индекс указывает на первый элемент массива (индекс 0), так как первый элемент всегда уникален сам по себе.Основной цикл:
for i in range(1, len(nums)): if nums[i] != nums[unique_index]: unique_index += 1 nums[unique_index] = nums[i]
Этот цикл начинается с индекса
1
и продолжается до конца массива. Он выполняет следующие шаги:- Сравнение текущего элемента с последним уникальным элементом: Для каждого элемента в массиве начиная с индекса 1, проверяется, отличается ли он от последнего уникального элемента, который находится на позиции
unique_index
. - Если элемент отличается от предыдущего уникального: Если текущий элемент (
nums[i]
) отличается от последнего уникального элемента (nums[unique_index]
), это означает, что мы нашли новый уникальный элемент. В этом случае:- Увеличиваем
unique_index
, чтобы указать на следующее место для уникального элемента. - Записываем текущий элемент на позицию
unique_index
, тем самым заменяя старый дубликат новым уникальным значением.
- Увеличиваем
Таким образом, этот цикл гарантирует, что на позициях с индексами от 0 до
unique_index
будут находиться все уникальные элементы массива в том же порядке.- Сравнение текущего элемента с последним уникальным элементом: Для каждого элемента в массиве начиная с индекса 1, проверяется, отличается ли он от последнего уникального элемента, который находится на позиции
Возвращаем количество уникальных элементов:
return unique_index + 1
После выполнения цикла переменная
unique_index
указывает на последний уникальный элемент в списке. Поскольку индексация в Python начинается с нуля, для получения количества уникальных элементов нужно добавить1
кunique_index
. Это и есть количество уникальных элементов в списке.
Пример:
Рассмотрим пример, чтобы понять, как работает функция:
nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
result = remove_duplicates(nums)
Шаги выполнения:
- В начале переменная
unique_index
равна 0, а списокnums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
. - После первого шага цикла (i=1),
nums[1] == nums[0]
, то есть элемент0
повторяется, ничего не меняется. На втором шаге (i=2),
nums[2] != nums[0]
(1 отличается от 0), поэтому:unique_index
увеличивается на 1, становится равным 1.- Элемент
1
записывается на позициюnums[1]
.
После этого список будет выглядеть так:
nums = [0, 1, 1, 1, 1, 2, 2, 3, 3, 4]
.- На третьем шаге (i=3),
nums[3] == nums[1]
(1 повторяется), ничего не меняется. - На четвёртом шаге (i=4), снова
nums[4] == nums[1]
, ничего не меняется. На пятом шаге (i=5),
nums[5] != nums[1]
(2 отличается от 1), поэтому:unique_index
увеличивается на 1, становится равным 2.- Элемент
2
записывается на позициюnums[2]
.
После этого список будет выглядеть так:
nums = [0, 1, 2, 1, 1, 2, 2, 3, 3, 4]
.Следующие шаги аналогичны. В итоге, после завершения цикла, массив будет иметь вид:
nums = [0, 1, 2, 3, 4, 2, 2, 3, 3, 4]
Функция вернёт
5
, что соответствует количеству уникальных элементов.
xsnm
,Это не правильное решение. Потеря уникальности элементов списка. Решение через присвоения не верно.
admin
,xsnm, Почему классическое решение с двумя указателями и со сложностью O(n) неправильное?
xsnm
,nums[unique_index] = nums[i] # "=" Это оператор присвоения. То есть элемент nums[unique_index] копия элемента nums[i]. Копия.....
# А это не уникальный элемент. Не оригинал. Где оригинал??????. Если решать то, первый элемент "1" в ряде оригинал и его нельзя трогать и так далее.
# "0" по умолчанию.
admin
,xsnm, Так уникальность это не про то, что нельзя использовать ссылки и тд, если хотите, вы можете посмотреть решебники на гитхабе подобных задач и там решения будут с более худшей сложность O(n^2), но найти решение с более эффективной сложностью вам будет труднее, если список не был бы заранее отсортирован, пришлось бы использовать множество. Если у вас есть более хорошее и правильное на ваш взгляд решение, приложите его пожалуйста, все ваши предложения я всегда рассматриваю на всех курсах и готов вносить правки, если вижу на основе ваших примеров реальные ошибки и неточности, особенно в плане этого задачника, ибо он ещё не прошёл полировку. Буду рад любой конструктивной критике).
admin
,xsnm, Отвечая на вопрос - "Где оригинал?"
Оригиналы элементов остаются в массиве, просто они могут перемещаться ближе к началу.
Алгоритм не удаляет элементы из памяти, а переупорядочивает их, чтобы уникальные шли подряд.
В Python числа (и неизменяемые объекты) передаются по значению, но в списке мы работаем с ссылками.
Присваивание
nums[unique_index] = nums[i]
не создает новый объект, а просто копирует ссылку.Если бы в списке были сложные объекты (например, списки или словари), то копировалась бы ссылка, но в данном случае (числа) разницы нет.
xsnm
,0 [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
1 [0, 0, 1, 1, 1, 2, 2, 3, 3, 4] 2
2 [0, 1, 1, 1, 1, 2, 2, 3, 3, 4] Вопрос. Было 3 шт. - "1" стало 4 шт.
1 [0, 1, 1, 1, 1, 2, 2, 3, 3, 4] 5
2 [0, 1, 2, 1, 1, 2, 2, 3, 3, 4]
1 [0, 1, 2, 1, 1, 2, 2, 3, 3, 4] 7 и так далее.....
2 [0, 1, 2, 3, 1, 2, 2, 3, 3, 4]
1 [0, 1, 2, 3, 1, 2, 2, 3, 3, 4] 9
2 [0, 1, 2, 3, 4, 2, 2, 3, 3, 4] а здесь одна "1". Она уникальна???? так с остальными
5
r [0, 1, 2, 3, 4, 2, 2, 3, 3, 4]
admin
,xsnm, Ответ на вопрос:
✅ Да, она уникальна в контексте задачи.
В первых
k=5
элементах ([0, 1, 2, 3, 4]
) "1" встречается только 1 раз (на позицииnums[1]
).Остальные "1" находятся после
k
-го элемента и не учитываются в результате.Алгоритм корректно перемещает уникальные элементы в начало, сохраняя их порядок.
Никакие элементы не копируются — просто меняются ссылки внутри массива.
"Оригиналы" не теряются, они либо остаются на месте, либо перемещаются в начало.
Всё, что после
nums[:k]
, не имеет значения (по условию задачи).DeaglenS
,def remove_duplicates(nums):
nums_set = set(nums)
nums[:] = sorted(nums_set)
return len(nums_set)