Задача: №16. 3Sum Closest #medium
Условие:
Решение:
Пояснение:
Сортировка массива:
Сначала мы сортируем массив 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), так как мы используем только несколько дополнительных переменных, и не используем дополнительную память, зависящую от размера входных данных.
Условие:
Учитывая целочисленный массив 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), так как мы используем только несколько дополнительных переменных, и не используем дополнительную память, зависящую от размера входных данных.
👍12❤2🔥1
Задача: №17. Letter Combinations of a Phone Number #medium
Условие:
Решение:
Пояснение:
Сортировка:
Сначала мы сортируем массив 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 элементов для хранения.
Условие:
Учитывая строку, содержащую цифры от 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 элементов для хранения.
👍3❤1
Задача: №18. 4Sum #medium
Условие:
Решение:
Пояснение:
Сортировка:
Сначала массив 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.
Условие:
Учитывая массив 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
Условие:
Решение:
Пояснение:
Определение размера списка:
Сначала мы проходимся по всему списку, чтобы найти его размер. Это необходимо, чтобы определить, какой узел нужно удалить.
Переменная size отслеживает количество узлов в списке.
Удаление первого узла:
Если n равно размеру списка, это означает, что нужно удалить первый узел. В этом случае возвращаем следующий узел от головы списка.
Поиск узла перед нужным для удаления:
Если n не равен размеру списка, нам нужно найти узел, который идет перед n-м узлом с конца. Мы делаем это, перемещаясь на size-n-1 узлов вперед в списке.
Удаление узла:
Устанавливаем следующий узел для найденного узла так, чтобы он указывал на следующий за следующим узел, effectively bypassing the node to be deleted.
Возвращение нового заголовка:
Возвращаем голову списка. Если был удален первый узел, новый заголовок будет начальным узлом списка после удаления.
Временная и пространственная сложность:
Временная сложность: O(L), где L — длина списка. Мы проходим по списку дважды: один раз для подсчета узлов и второй раз для удаления узла.
Пространственная сложность: O(1), поскольку мы используем константное количество дополнительного пространства, не зависящего от размера входных данных.
Условие:
Учитывая заголовок связанного списка, удалите 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
Условие:
Решение:
Пояснение:
Определение маппинга:
Словарь dic используется для сопоставления каждой скобке их числового представления. Открывающие скобки ('(', '[', '{') получают нечётные значения, а закрывающие скобки (')', ']', '}') — соответствующие четные значения, вдвое большие их.
Обработка строки:
Перебираем каждый символ в строке s.
Для каждого символа получаем его числовое значение из словаря dic.
Проверка на открывающую скобку:
Если значение символа нечётное, это открывающая скобка. Мы добавляем это значение в список res.
Проверка на закрывающую скобку:
Если значение символа четное, это закрывающая скобка. Мы проверяем:
Если список res не пуст и последний элемент в списке равен половине текущего значения (что означает, что открывающая скобка соответствует закрывающей скобке), удаляем последний элемент из списка res.
Если это не так, возвращаем False, так как закрывающая скобка не соответствует последней открывающей скобке.
Завершение:
Если после обработки всей строки список res пуст, это значит, что все скобки корректно закрыты, и возвращаем True.
Если список res не пуст, это означает, что остались незакрытые открывающие скобки, и возвращаем False.
Временная и пространственная сложность:
Временная сложность: O(n), где n — длина строки. Мы проходим по строке один раз.
Пространственная сложность: O(n), где n — количество открывающих скобок, так как в худшем случае все они могут оказаться в списке res.
Условие:
Учитывая строку 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.
LeetCode
Valid Parentheses - LeetCode
Can you solve this real interview question? Valid Parentheses - Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
An input string is valid if:
1. Open brackets must be closed by the…
An input string is valid if:
1. Open brackets must be closed by the…
👍1🔥1
Задача: 46. Permutations #Medium
Условие:
Решение:
Пояснение:
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, содержащий все перестановки массива.
Условие:
Учитывая массив 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