Python | LeetCode
10.1K subscribers
152 photos
1.05K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.me/+20tRfhrwPpM4NDQy
Вопросы собесов t.me/+cnJC0_ZeZ_I0OGY6
Вакансии t.me/+cXGKkrOY2-w3ZTky
Download Telegram
Channel created
Задача: №16. 3Sum Closest #medium

Условие:
Учитывая целочисленный массив nums длины n и целочисленную цель, найдите три целых числа в nums, сумма которых наиболее близка к цели. Возвращает сумму трех целых чисел. Вы можете предположить, что каждый вход будет иметь ровно одно решение.


Решение:
```class Solution:
def threeSumClosest(self, num, target):
num.sort()
result = num[0] + num[1] + num[2]
for i in range(len(num) - 2):
j, k = i+1, len(num) - 1
while j < k:
sum = num[i] + num[j] + num[k]
if sum == target:
return sum

if abs(sum - target) < abs(result - target):
result = sum

if sum < target:
j += 1
elif sum > target:
k -= 1
else:
return result

return result
```


Пояснение:
Сортировка массива:

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

Инициализируем переменную result суммой первых трех элементов отсортированного массива. Это будет нашей начальной ближайшей суммой.
Проход по массиву:

Используем цикл for, чтобы пройти по массиву. Для каждого элемента используем два указателя j и k:
j начинается сразу после текущего элемента i.
k начинается с конца массива.
Два указателя:

Внутри цикла while, пока j меньше k, вычисляем сумму sum элементов num[i], num[j] и num[k].
Если сумма sum равна target, то возвращаем sum, так как мы нашли точное соответствие.
Обновление результата:

Если текущая сумма sum ближе к target, чем предыдущая ближайшая сумма result, обновляем result.
Сдвиг указателей:

Если sum меньше target, сдвигаем указатель j вправо, чтобы увеличить сумму.
Если sum больше target, сдвигаем указатель k влево, чтобы уменьшить сумму.
Возврат результата:

После завершения всех итераций возвращаем result, которая будет содержать сумму трех чисел, ближайшую к target.
Временная и пространственная сложность:
Временная сложность: O(n^2), где n — длина массива. Сортировка занимает O(n log n), и основной алгоритм с двумя указателями выполняется за O(n^2).
Пространственная сложность: O(1), так как мы используем только несколько дополнительных переменных, и не используем дополнительную память, зависящую от размера входных данных.
👍122🔥1
Задача: №17. Letter Combinations of a Phone Number #medium

Условие:
Учитывая строку, содержащую цифры от 2 до 9 включительно, верните все возможные комбинации букв, которые может представлять число. Верните ответ в любом порядке. Соответствие цифр буквам (как на кнопках телефона) приведено ниже. Обратите внимание, что 1 не соответствует никаким буквам.


Решение:
class Solution(object):
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
nums.sort()
self.dfs(nums, [], res)
return res

def dfs(self, nums, path, res):
res.append(path)
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]:
continue
self.dfs(nums[i+1:], path + [nums[i]], res)


Пояснение:
Сортировка:

Сначала мы сортируем массив nums. Сортировка помогает легко обрабатывать дубликаты, так как одинаковые элементы будут расположены рядом.
Рекурсивный метод dfs:

Метод dfs используется для рекурсивного построения всех возможных подмножеств. В dfs, path представляет текущее подмножество, а res — список всех подмножеств.
Добавление подмножества в результат:

На каждом уровне рекурсии мы добавляем текущее подмножество path в результат res.
Обработка дубликатов:

Перед продолжением рекурсии проверяем, является ли текущий элемент nums[i] дубликатом предыдущего элемента. Если это так, пропускаем его, чтобы избежать повторного добавления одинаковых подмножеств в res.
Рекурсивное построение подмножеств:

Для каждого элемента в nums, начиная с текущего индекса, мы вызываем dfs для следующих элементов массива (nums[i+1:]). Это означает, что мы рассматриваем подмножества, включающие текущий элемент, и продолжаем строить подмножества без текущего элемента.
Путь (path) и отсек (nums):

Каждый раз при вызове dfs создается новое подмножество, добавляя текущий элемент к path. Затем этот новый список передается в следующий уровень рекурсии, что позволяет построить все возможные подмножества.
Временная и пространственная сложность:
Временная сложность: O(2^n), где n — количество элементов в nums. Это связано с тем, что существует 2^n подмножеств для массива из n элементов.
Пространственная сложность: O(2^n * n), так как для каждой из 2^n возможных подмножеств может потребоваться до n элементов для хранения.
👍31
Задача: №18. 4Sum #medium

Условие:
Учитывая массив nums из n целых чисел, верните массив всех уникальных четверок [nums[a], nums[b], nums[c], nums[d]] таких, что:
0 <= a, b, c, d < n
a, b, c и d различны.
nums[a] + nums[b] + nums[c] + nums[d] == цель
Вы можете вернуть ответ в любом порядке.


Решение:
def fourSum(self, nums, target):
nums.sort()
results = []
self.findNsum(nums, target, 4, [], results)
return results

def findNsum(self, nums, target, N, result, results):
if len(nums) < N or N < 2: return

# solve 2-sum
if N == 2:
l,r = 0,len(nums)-1
while l < r:
if nums[l] + nums[r] == target:
results.append(result + [nums[l], nums[r]])
l += 1
r -= 1
while l < r and nums[l] == nums[l - 1]:
l += 1
while r > l and nums[r] == nums[r + 1]:
r -= 1
elif nums[l] + nums[r] < target:
l += 1
else:
r -= 1
else:
for i in range(0, len(nums)-N+1): # careful about range
if target < nums[i]*N or target > nums[-1]*N: # take advantages of sorted list
break
if i == 0 or i > 0 and nums[i-1] != nums[i]: # recursively reduce N
self.findNsum(nums[i+1:], target-nums[i], N-1, result+[nums[i]], results)
return

Пояснение:
Сортировка:

Сначала массив nums сортируется. Сортировка упрощает обработку дубликатов и ускоряет выполнение с помощью двоичного поиска.
Рекурсивная функция findNsum:

Функция findNsum рекурсивно находит комбинации, сумма которых равна заданному target. В зависимости от значения N, она обрабатывает различные случаи:
Для N = 2: Это подзадача "Two Sum". Мы используем два указателя (l и r), чтобы найти пары чисел в массиве, которые суммируются до target.
Если текущая пара чисел nums[l] и nums[r] суммируется до target, добавляем эту пару к результатам.
Двигаем указатели влево и вправо, пропуская дубликаты, чтобы избежать повторных комбинаций.
Для N > 2: Функция рекурсивно вызывает себя, уменьшая N на 1 и продолжая искать комбинации среди оставшихся элементов массива. Мы также проверяем, находятся ли текущий элемент и его комбинации в допустимом диапазоне (для оптимизации).
Условия выхода из рекурсии:

Если длина массива меньше N или N меньше 2, функция завершает выполнение, так как это не имеет смысла для дальнейшего поиска комбинаций.
Мы используем условия, чтобы прекратить выполнение, если текущий элемент слишком велик или слишком мал для достижения целевого значения при данном числе элементов (N).
Уникальные комбинации:

Чтобы избежать дубликатов, проверяем, является ли текущий элемент уникальным по сравнению с предыдущими.
Сбор результатов:

Результаты для каждого вызова функции findNsum добавляются в общий список results.
🔥1
Задача: №19. Remove Nth Node From End of List #medium

Условие:
Учитывая заголовок связанного списка, удалите n-й узел из конца списка и верните его заголовок.


Решение:
# class ListNode:
# def init(self, x):
# self.val = x
# self.next = None

class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
if head.next == None:
return None
tmp = head
size = 0

# find the size of the linked list
while tmp:
size += 1
tmp = tmp.next
tmp = head

#if we have to remove the first node:
if n == size:
return head.next

for i in range(size-n-1):
tmp = tmp.next
tmp.next = tmp.next.next
return head


Пояснение:
Определение размера списка:

Сначала мы проходимся по всему списку, чтобы найти его размер. Это необходимо, чтобы определить, какой узел нужно удалить.
Переменная size отслеживает количество узлов в списке.
Удаление первого узла:

Если n равно размеру списка, это означает, что нужно удалить первый узел. В этом случае возвращаем следующий узел от головы списка.
Поиск узла перед нужным для удаления:

Если n не равен размеру списка, нам нужно найти узел, который идет перед n-м узлом с конца. Мы делаем это, перемещаясь на size-n-1 узлов вперед в списке.
Удаление узла:

Устанавливаем следующий узел для найденного узла так, чтобы он указывал на следующий за следующим узел, effectively bypassing the node to be deleted.
Возвращение нового заголовка:

Возвращаем голову списка. Если был удален первый узел, новый заголовок будет начальным узлом списка после удаления.
Временная и пространственная сложность:
Временная сложность: O(L), где L — длина списка. Мы проходим по списку дважды: один раз для подсчета узлов и второй раз для удаления узла.
Пространственная сложность: O(1), поскольку мы используем константное количество дополнительного пространства, не зависящего от размера входных данных.
👍2🔥2
Задача: №20. Valid Parentheses #easy

Условие:
Учитывая строку s, содержащую только символы '(', ')', '{', '}', '[' и ']', определите, является ли входная строка допустимой. Входная строка действительна, если: Открытые скобки должны закрываться скобками того же типа.
Открытые скобки должны закрываться в правильном порядке.
Каждой закрывающей скобке соответствует открытая скобка того же типа.


Решение:

  def isValid(self, s: str) -> bool:
dic = {'(':1 , ')':2 , '[':3 , ']':6 , '{':5 , '}':10}
res = []
for one in s:
temp = dic[one]
if(temp %2 ):
res.append(temp)
else:
if(len(res) and res[-1]==temp//2):
del res[-1]
else:
return False
return res==[]


Пояснение:
Определение маппинга:

Словарь dic используется для сопоставления каждой скобке их числового представления. Открывающие скобки ('(', '[', '{') получают нечётные значения, а закрывающие скобки (')', ']', '}') — соответствующие четные значения, вдвое большие их.
Обработка строки:

Перебираем каждый символ в строке s.
Для каждого символа получаем его числовое значение из словаря dic.
Проверка на открывающую скобку:

Если значение символа нечётное, это открывающая скобка. Мы добавляем это значение в список res.
Проверка на закрывающую скобку:

Если значение символа четное, это закрывающая скобка. Мы проверяем:
Если список res не пуст и последний элемент в списке равен половине текущего значения (что означает, что открывающая скобка соответствует закрывающей скобке), удаляем последний элемент из списка res.
Если это не так, возвращаем False, так как закрывающая скобка не соответствует последней открывающей скобке.
Завершение:

Если после обработки всей строки список res пуст, это значит, что все скобки корректно закрыты, и возвращаем True.
Если список res не пуст, это означает, что остались незакрытые открывающие скобки, и возвращаем False.
Временная и пространственная сложность:
Временная сложность: O(n), где n — длина строки. Мы проходим по строке один раз.
Пространственная сложность: O(n), где n — количество открывающих скобок, так как в худшем случае все они могут оказаться в списке res.
👍1🔥1
Задача: 46. Permutations #Medium

Условие:
Учитывая массив nums, состоящий из различных целых чисел, верните все возможные перестановки. Вы можете вернуть ответ в любом порядке.



Решение:
class Solution: 
def permute(self, nums: List[int]) -> List[List[int]]:
result = []

def permute_rec(nums, current_index, result):
if current_index == len(nums) - 1:
result.append(nums.copy())
return

for index in range(current_index, len(nums)):
nums[current_index], nums[index] = nums[index], nums[current_index]
permute_rec(nums, current_index + 1, result)
nums[current_index], nums[index] = nums[index], nums[current_index]

permute_rec(nums, 0, result)
return result



Пояснение:
1
. Инициализируем список result для хранения результирующих перестановок.
2. Определяем рекурсивную функцию permute_rec(nums, current_index, result):
- Базовый случай: Если current_index достигает последнего индекса массива, значит мы нашли перестановку, и добавляем копию текущего массива nums в result.
- В цикле перебираем все элементы массива, начиная с current_index. Для каждого элемента:
- Меняем местами текущий элемент и элемент на индексе index.
- Вызываем permute_rec с обновленным массивом и увеличенным current_index.
- Меняем местами элементы обратно.
3. Вызываем permute_rec(nums, 0, result) для запуска рекурсии с начальным индексом 0.
4. Возвращаем result, содержащий все перестановки массива.
🔥1
Задача: 45. Jump Game II #Medium

Условие:

Вам предоставляется массив целых чисел nums с индексом 0 и длиной n. Изначально вы располагаетесь в nums[0].
Каждый элемент nums[i] представляет максимальную длину прямого перехода от индекса i. Другими словами, если вы находитесь в nums[i], вы можете перейти к любому nums[i + j], где:
0 <= j <= nums[i] иi + j < n
Возвращает минимальное количество переходов для достижения nums[n - 1]. Тестовые примеры генерируются таким образом, чтобы вы могли достичь nums[n - 1].



Решение:
class Solution: 
def jump(self, nums: List[int]) -> int:
ans = 0
end = 0
farthest = 0

# Implicit BFS
for i in range(len(nums) - 1):
farthest = max(farthest, i + nums[i])
if farthest >= len(nums) - 1:
ans += 1
break
if i == end: # Visited all the items on the current level
ans += 1 # Increment the level
end = farthest # Make the queue size for the next level

return ans



Пояснение:
1
. Инициализируем ans как 0, который будет хранить количество прыжков.
2. Инициализируем end как 0, который будет хранить границу текущего уровня прыжков.
3. Инициализируем farthest как 0, который будет хранить самую дальнюю позицию, достижимую с текущего уровня прыжков.
4. Перебираем элементы массива nums до предпоследнего элемента:
- Обновляем farthest как максимум из текущего farthest и текущей позиции i плюс значение nums[i].
- Если farthest больше или равен длине массива минус 1 (последний индекс), значит, мы достигли конца, и увеличиваем ans на 1.
- Если i равно end, значит, мы завершили текущий уровень прыжков, и увеличиваем ans на 1. Также обновляем end до farthest, чтобы обозначить границу следующего уровня.
5. Возвращаем ans.
👍1🔥1
Задача: 44. Wildcard Matching #Hard

Условие:
При наличии входных строк и шаблона (p) реализуйте сопоставление шаблонов с подстановочными знаками с поддержкой "?" и "*", где:
"?" Соответствует любому отдельному символу."*" Соответствует любой последовательности символов (включая пустую последовательность).
Соответствие должно охватывать всю входную строку (не частичное).




Решение:
class Solution: 
def isMatch(self, s: str, p: str) -> bool:
memo = [[None] * (len(p) + 1) for _ in range(len(s) + 1)]
return self.f(s, 0, p, 0, memo)

def f(self, s, i, p, j, memo):
if i == len(s) and j == len(p): //both strings end : match successfull
return True

//Pattern is over but string is left: match unsuccessful.
//Point to note : if String is over and pattern is left,
//it 'might' still be possible to match the string.
//Take for eg. s = "" and p = "*******".
if j == len(p):
return False

if memo[i][j] is None: //subproblem not solved earlier.
flag = False

//match single character, but string s should be left,
//else we get IndexError
if i < len(s) and (s[i] == p[j] or p[j] == '?'):
flag = self.f(s, i + 1, p, j + 1, memo)

elif p[j] == '*': // can match variable no. of characters, so loop over them and see when it matches.
for t in range(i, len(s) + 1):
if self.f(s, t, p, j + 1, memo):
flag = True
break
//if it doesn't ever match, the flag will be False by default.
memo[i][j] = flag

return memo[i][j]


Пояснение:
1. Динамическое программирование: Используем двумерный массив memo для хранения промежуточных результатов.

2. Рекурсивная функция f: Определяем рекурсивную функцию f(s, i, p, j, memo), которая принимает следующие параметры:
- s: Исходная строка.
- i: Индекс текущего символа в s.
- p: Шаблон.
- j: Индекс текущего символа в p.
- memo: Массив для хранения промежуточных результатов.

3. Рекурсивная логика:
- Базовый случай: Если i и j достигли концов s и p соответственно, то строка соответствует шаблону.
- Если j достиг конца p, но i не достиг конца s, то строка не соответствует шаблону.
- Если memo[i][j] не равно None, то мы уже рассчитали результат для этих индексов и можем вернуть его.
- Проверяем совпадение символов:
- Если символы на индексах i в s и j в p совпадают или символ в p является подстановочным знаком ?, то мы двигаемся дальше.
- Если символ в p является звездочкой *, то мы можем пропустить произвольное количество символов в s, поэтому перебираем все возможные варианты пропуска и проверяем соответствие.

4. Заполнение memo: Сохраняем результат для данного вызова f в массиве memo для использования в будущих вызовах.
🤯7👍3🔥2
Задача: №21. Merge Two Sorted Lists #easy

Условие:
Вам даны заголовки двух отсортированных связанных списков list1 и list2. Объедините два списка в один отсортированный список. Список должен быть составлен путем сращивания узлов первых двух списков. Возвращает заголовок объединенного связанного списка.


Решение:
class Solution(object):
def mergeTwoLists(self, l1, l2):
dummy = ListNode(0)
cur = dummy
while l1 and l2:
if l1.val <= l2.val:
cur.next = l1
l1 = l1.next
else:
cur.next = l2
l2 = l2.next
cur = cur.next
cur.next = l1 or l2

return dummy.next


Пояснение:
Создание фиктивного узла:

Создается фиктивный узел dummy для упрощения операции добавления узлов к результатирующему списку. Этот узел будет служить началом итогового списка.
Указатель cur инициализируется на dummy и будет использоваться для построения итогового списка.
Слияние списков:

Цикл выполняется до тех пор, пока не достигнут конец одного из списков (l1 или l2).
Внутри цикла происходит сравнение значений текущих узлов двух списков:
Если значение узла из списка l1 меньше или равно значению узла из списка l2, то узел из списка l1 присоединяется к итоговому списку.
Иначе, узел из списка l2 присоединяется к итоговому списку.
Перемещение указателя cur вперед для добавления следующего узла.
Добавление оставшихся узлов:

После завершения цикла один из списков может быть непустым. В этом случае остается добавить оставшиеся узлы этого списка к итоговому списку.
Это делается с помощью
cur.next = l1 or l2, что означает, что если l1 не пуст, то добавляются узлы из l1, иначе добавляются узлы из l2.
Возвращение результата:

Возвращается следующий узел от фиктивного узла, что представляет собой начальный узел итогового списка, который содержит все элементы из обоих списков в отсортированном порядке.
Временная и пространственная сложность:
Временная сложность: O(n + m), где n и m — количество узлов в l1 и l2 соответственно. Этот алгоритм проходит через каждый узел списков один раз.
Пространственная сложность: O(1) для дополнительных данных, так как используется только фиксированное количество переменных для указателей и временных операций
.
🤔5👍2🔥1👀1
Задача: 22. Generate Parentheses #medium

Условие:

Учитывая n пар круглых скобок, напишите функцию, которая генерирует все комбинации правильных круглых скобок.


Решение:
def generateParenthesis(self, n: int) -> List[str]: 
def dfs(left, right, s):
if len(s) == n * 2:
res.append(s)
return

if left < n:
dfs(left + 1, right, s + '(')

if right < left:
dfs(left, right + 1, s + ')')

res = []
dfs(0, 0, '')
return res


Пояснение:
1. Функция generateParenthesis принимает на вход целое число n, которое представляет количество пар скобок, которые нужно сгенерировать.

2. Внутри функции есть вспомогательная функция, называемая dfs (Depth-First Search). Эта функция выполняет фактическую генерацию скобок.

3. Функция dfs принимает три параметра:
- left: количество открывающих скобок в текущей строке.
- right: количество закрывающих скобок в текущей строке.
- s: текущая строка скобок.

4. Базовый случай рекурсии - когда длина строки s равна n * 2, что означает, что мы сгенерировали полную строку скобок. В этом случае мы добавляем текущую строку s в список res и выходим из функции.

5. Первый рекурсивный случай - когда left меньше n. Это значит, что мы можем добавить открывающую скобку к строке. Поэтому мы вызываем dfs с left + 1, right без изменений и s + '('.

6. Второй рекурсивный случай - когда right меньше left. Это означает, что мы можем добавить закрывающую скобку к строке, так как количество закрывающих скобок меньше количества открывающих. Поэтому мы вызываем dfs с left без изменений, right + 1 и s + ')'.

7. Функция dfs первоначально вызывается с left = 0, right = 0 и пустой строкой ''.

8. Рекурсивные вызовы продолжаются, пока не будет достигнут базовый случай, и все допустимые сочетания скобок будут добавлены в список res.

9. Наконец, функция generateParenthesis возвращает список res, содержащий все допустимые строки скобок длины n * 2.
👍181🔥1
Задача: 23. Merge k Sorted Lists #medium

Условие
:
Вам дан массив из k списков связанных списков, каждый связанный список отсортирован в порядке возрастания.Объедините все связанные списки в один отсортированный связанный список и верните его.


Решение:
class ListNode: 
def init(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
n = len(lists)

newList = ListNode()
new = newList

while n:
# Find list with smallest value:
smallestNode = ListNode(float('inf'))
smallestIndex = -1
for i in range(n):
listNode = lists[i]
if listNode and listNode.val < smallestNode.val:
smallestNode = listNode
smallestIndex = i

if smallestIndex == -1:
break

new.next = ListNode(smallestNode.val)
new = new.next

if smallestNode.next:
lists[smallestIndex] = smallestNode.next
else:
lists.pop(smallestIndex)
n-=1

return newList.next

Пояснение:
1. Определяется класс ListNode, который представляет узел связного списка. У него есть два атрибута: val (значение узла) и next (ссылка на следующий узел).

2. Определяется класс Solution, который содержит метод mergeKLists.

3. Метод mergeKLists принимает на вход список lists, который является списком связных списков. Каждый список в lists является необязательным (Optional[ListNode]).

4. Вначале определяется длина списка lists и сохраняется в переменной n.

5. Создается новый связный список newList и переменная new, которая будет указывать на последний добавленный узел в newList.

6. Запускается цикл, который продолжается, пока n (количество списков в lists) не станет равным 0.

7. Внутри цикла производится поиск списка с наименьшим значением в первом узле. Для этого:
- Создается переменная smallestNode со значением бесконечности.
- Создается переменная smallestIndex со значением -1.
- Перебираются все списки в lists.
- Если текущий список не пуст и значение в его первом узле меньше, чем в smallestNode, то smallestNode и smallestIndex обновляются.

8. Если smallestIndex равен -1, это означает, что все списки в lists пусты, поэтому цикл завершается.

9. Иначе создается новый узел с значением, взятым из smallestNode, и добавляется в конец newList.

10. Если у smallestNode есть следующий узел, то соответствующий список в lists обновляется, чтобы указывать на этот следующий узел.
Если у smallestNode нет следующего узла, то этот список удаляется из lists, и n уменьшается на 1.

11. После завершения цикла возвращается newList.next, так как первый узел newList был пустым вспомогательным узлом.
👍8🤯32
Задача: 24. Swap Nodes in Pairs #medium

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


Решение:
 def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: 
if not (head and head.next):
return head
head, head.next, head.next.next = head.next, head, self.swapPairs(head.next.next)
return head

Пояснение:
1. Функция swapPairs принимает на вход головной узел связного списка head и возвращает новую голову списка после перестановки пар узлов.

2. Сначала проверяется, есть ли головной узел head и есть ли следующий за ним узел head.next. Если нет, то возвращается исходный head, так как нечего менять.

3. Если есть как минимум два узла, то происходит перестановка:
- Временная переменная temp сохраняет второй узел head.next.
- Головной узел head становится третьим узлом, а его следующий узел head.next становится первым узлом.
- Следующий за вторым узлом указатель head.next.next обновляется рекурсивным вызовом self.swapPairs(head.next.next), чтобы перестроить оставшуюся часть списка.
- Головной узел head возвращается в качестве новой головы списка.

4. Рекурсивный вызов self.swapPairs(head.next.next) будет происходить, пока не останется меньше двух узлов в списке. Это обеспечивает перестановку всех пар узлов.
👍11🔥1
Задача: 25. Reverse Nodes in k-Group #hard

Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка k за раз и верните измененный список.

k — целое положительное число, меньшее или равное длине связанного списка. Если количество узлов не кратно k, то пропущенные узлы в конечном итоге должны остаться такими, какие они есть.

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


Решение:
class Solution: 
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if k == 1:
return head

# sets first.next as last.next, and others next immediately preceding
# sets pre_first.next or head to point to the new first
def reverse(first, last, pre_first):
nonlocal head
# take care of link to new first node
if pre_first:
pre_first.next = last
else:
head = last

prev, curr = first, first.next
first.next = last.next # take care of link to remaining nodes
for _ in range(k-1):
curr.next, prev, curr = prev, curr, curr.next

count = 1
curr = first = head
pre_first = None
while curr:
if count == k:
# we have reached the kth node, now it's time to reverse and reset
reverse(first, curr, pre_first)
pre_first = first
curr, first = first.next, first.next
count = 1
else:
curr = curr.next
count += 1

return head

Пояснение:
1. Определяется класс Solution, который содержит метод reverseKGroup.

2. Метод reverseKGroup принимает на вход головной узел связного списка head и целое число k, которое определяет, по сколько узлов нужно разворачивать список.

3. Сначала проверяется, если k равно 1, то возвращается исходный head, так как нечего менять.

4. Далее определяется вспомогательная функция reverse, которая принимает 3 параметра:
- first: указатель на первый узел в группе, которую нужно развернуть.
- last: указатель на последний узел в группе.
- pre_first: указатель на узел, предшествующий первому узлу в группе.

5. Функция reverse выполняет следующие действия:
- Если есть pre_first, то его next указатель переводится на last, чтобы связать новый первый узел с предыдущей частью списка.
- Если pre_first равен None, то head обновляется, чтобы указывать на новый первый узел (last).
- Затем происходит непосредственно разворот группы из k узлов с помощью изменения указателей next.

6. В основном методе reverseKGroup инициализируются следующие переменные:
- count: счетчик количества пройденных узлов.
- curr: указатель на текущий узел.
- first: указатель на первый узел в группе.
- pre_first: указатель на узел, предшествующий первому узлу в группе.

7. Затем запускается цикл, который проходит по всем узлам списка:
- Если count достиг k, значит мы достигли конца группы, поэтому вызываем функцию reverse, передавая необходимые параметры.
- После вызова reverse обновляем pre_first, curr и first, чтобы подготовиться к следующей группе.
- Если count еще не достиг k, то просто перемещаем curr на следующий узел и увеличиваем count.

8. После завершения цикла возвращается обновленная head списка.
👍71🔥1
Задача: 26. Remove Duplicates from Sorted Array #easy

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

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

Измените массив nums так, чтобы первые k элементов nums содержали уникальные элементы в том порядке, в котором они присутствовали в nums изначально. Остальные элементы nums не важны, как и размер nums.
Вернуть К.


Решение:
from typing import List 

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
i = 0
while i < len(nums) - 1:
if nums[i] == nums[i + 1]:
print(nums[i + 1])
nums.pop(i + 1)
else:
i += 1

return len(nums)

Пояснение:
1. Определяется класс Solution, который содержит метод removeDuplicates.

2. Метод removeDuplicates принимает на вход список целых чисел nums.

3. Инициализируется переменная i, которая будет использоваться для итерации по списку.

4. Запускается цикл while, который продолжается, пока i меньше длины списка nums минус 1. Это необходимо, чтобы избежать выхода за пределы списка при проверке следующего элемента.

5. Внутри цикла while проверяется, равны ли элементы по индексам i и i + 1 в списке nums.

6. Если элементы равны, то это означает, что найден дубликат. В этом случае:
- Выводится на экран значение элемента по индексу i + 1, чтобы показать, какой элемент был удален.
- Из списка nums удаляется элемент по индексу i + 1 с помощью метода pop(i + 1).

7. Если элементы не равны, то просто увеличивается значение i на 1, чтобы перейти к следующему элементу.

8. После завершения цикла while возвращается длина обновленного списка nums, то есть количество уникальных элементов.
👍3🔥1
Задача: 27. Remove Element #easy

Условие:
Учитывая целочисленный массив nums и целочисленное значение, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в виде чисел, которые не равны val.

Учитывайте количество элементов в nums, которые не равны val be k. Чтобы вас приняли, вам необходимо сделать следующее:

Измените массив nums так, чтобы первые k элементов nums содержали элементы, не равные val. Остальные элементы nums не важны, как и размер nums.
Вернуть К.


Решение:
class Solution: 
def removeElement(self, nums: List[int], val: int) -> int:
k = 0

for i in range(len(nums)):
if nums[i] != val:
nums[k] = nums[i]
k += 1


Пояснение:
Шаг 1: Инициализировать указатель

* Создаём указатель k, который будет отслеживать индекс элемента, который мы хотим сохранить в отфильтрованном массиве. Изначально он равен 0.

Шаг 2: Перебрать массив

* Используем цикл for, чтобы перебрать все элементы массива nums.

Шаг 3: Проверить равенство значений

* Для каждого элемента проверяем, не равен ли он значению val, которое нужно удалить. Если это не так, то:

Шаг 4: Скопировать элемент

* Копируем элемент nums[i] в позицию nums[k].

Шаг 5: Увеличить указатель

* Увеличиваем указатель k на 1, чтобы перейти к следующей позиции в отфильтрованном массиве.

Шаг 6: Вернуть указатель

* После перебора всех элементов возвращаем указатель k, который указывает на количество элементов в отфильтрованном массиве.


return k
👍8🔥1
Задача: 28. Find the Index of the First Occurrence in a String #easy

Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -1, если игла не является частью стога сена.


Решение:
class Solution(object): 
def strStr(self, haystack, needle):
# makes sure we don't iterate through a substring that is shorter than needle
for i in range(len(haystack) - len(needle) + 1):
# check if any substring of haystack with the same length as needle is equal to needle
if haystack[i : i+len(needle)] == needle:
# if yes, we return the first index of that substring
return i
# if we exit the loop, return -1
return -1

Пояснение:
1. Определяется класс Solution, который содержит метод strStr.

2. Метод strStr принимает на вход две строки: haystack (в которой ищется подстрока) и needle (сама искомая подстрока).

3. Запускается цикл for, который итерируется от 0 до длины haystack минус длина needle плюс 1. Это необходимо, чтобы избежать итерации по подстрокам, длина которых меньше длины needle, поскольку они точно не могут содержать needle.

4. Внутри цикла for проверяется, равны ли подстрока haystack[i:i+len(needle)] (подстрока, начинающаяся с индекса i и имеющая длину len(needle)) и строка needle.

5. Если подстроки равны, то это значит, что мы нашли вхождение needle в haystack, и возвращается индекс i, который является первым индексом этой подстроки.

6. Если цикл for завершается, не найдя вхождения needle в haystack, то возвращается -1, что означает, что needle не найден в haystack.
🤔2🔥1