Задача: 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
Условие:
Решение:
Пояснение:
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 списка.
Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка 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 списка.
👍7❤1🔥1
Задача: 26. Remove Duplicates from Sorted Array #easy
Условие:
Решение:
Пояснение:
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, то есть количество уникальных элементов.
Условие:
Учитывая целочисленный массив чисел, отсортированный в неубывающем порядке, удалите дубликаты на месте так, чтобы каждый уникальный элемент появлялся только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в числах.
Считайте, что количество уникальных элементов чисел равно 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
Условие:
Решение:
Шаг 1: Инициализировать указатель
* Создаём указатель k, который будет отслеживать индекс элемента, который мы хотим сохранить в отфильтрованном массиве. Изначально он равен 0.
Шаг 2: Перебрать массив
* Используем цикл for, чтобы перебрать все элементы массива nums.
Шаг 3: Проверить равенство значений
* Для каждого элемента проверяем, не равен ли он значению val, которое нужно удалить. Если это не так, то:
Шаг 4: Скопировать элемент
* Копируем элемент nums[i] в позицию nums[k].
Шаг 5: Увеличить указатель
* Увеличиваем указатель k на 1, чтобы перейти к следующей позиции в отфильтрованном массиве.
Шаг 6: Вернуть указатель
* После перебора всех элементов возвращаем указатель k, который указывает на количество элементов в отфильтрованном массиве.
return k
Условие:
Учитывая целочисленный массив 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. Определяется класс 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.
Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -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
Задача: 29. Divide Two Integers #medium
Условие:
Решение:
Пояснение:
1. Определяется класс Solution, который содержит метод divide.
2. Метод divide принимает на вход два целых числа: dividend (делимое) и divisor (делитель).
3. Сначала проверяется, равен ли divisor 1. Если да, то:
- Вычисляется новое значение dividend как произведение divisor и dividend.
- Проверяется, не превышает ли dividend диапазон 32-битных целых чисел (от -2^31 до 2^31 - 1). Если превышает, то возвращается максимальное или минимальное значение в этом диапазоне.
- Возвращается вычисленное значение dividend.
4. Если divisor не равен 1, то инициализируются следующие переменные:
- quotient: переменная, которая будет хранить результат деления.
- sign: переменная, которая будет хранить знак результата деления (-1 или 1).
5. Определяется знак результата деления:
- Если произведение dividend и divisor меньше 0, то знак результата будет отрицательным, и sign устанавливается в -1.
- Иначе знак результата будет положительным, и sign устанавливается в 1.
6. Абсолютные значения dividend и divisor записываются в соответствующие переменные для дальнейших вычислений.
7. Инициализируются переменные i (счетчик) и sum_all (сумма произведений divisor и i).
8. Запускается цикл while, который продолжается, пока sum_all меньше или равно dividend:
- Если dividend - sum_all меньше, чем divisor * i, то i сбрасывается в 1.
- quotient увеличивается на i.
- sum_all увеличивается на divisor * i.
- i увеличивается на 1.
9. После выхода из цикла while возвращается значение (quotient - 1) * sign, что является результатом деления dividend на divisor с учетом знака.
Условие:
Учитывая два целых числа: делимое и делитель, разделите два целых числа, не используя операторы умножения, деления и модификатора.
Целочисленное деление должно сокращаться до нуля, что означает потерю дробной части. Например, 8,345 будет сокращено до 8, а -2,7335 будет сокращено до -2.
Возвращает частное после деления делимого на делитель.
Примечание. Предположим, мы имеем дело со средой, которая может хранить целые числа только в пределах 32-битного целого диапазона со знаком: [−2^31, 2^31 − 1]. В этой задаче, если частное строго больше 2^31 - 1, верните 2^31 - 1, а если частное строго меньше -2^31, верните -2^31.
Решение:
class Solution(object):
def divide(self, dividend, divisor):
"""
:type dividend: int
:type divisor: int
:rtype: int
"""
if abs(divisor) == 1:
dividend = divisor * dividend
if dividend > 2**31 - 1:
return 2**31 - 1
if dividend < -2**31:
return -2**31
return dividend
quotient = 0
sign = 1
if dividend * divisor < 0:
sign = -1
if dividend < 0: dividend = -dividend
if divisor < 0: divisor = -divisor
i = 1
sum_all = 0
while sum_all <= dividend:
if dividend - sum_all < divisor * i:
i = 1
quotient += i
sum_all += divisor * i
i += 1
return (quotient - 1) * sign
Пояснение:
1. Определяется класс Solution, который содержит метод divide.
2. Метод divide принимает на вход два целых числа: dividend (делимое) и divisor (делитель).
3. Сначала проверяется, равен ли divisor 1. Если да, то:
- Вычисляется новое значение dividend как произведение divisor и dividend.
- Проверяется, не превышает ли dividend диапазон 32-битных целых чисел (от -2^31 до 2^31 - 1). Если превышает, то возвращается максимальное или минимальное значение в этом диапазоне.
- Возвращается вычисленное значение dividend.
4. Если divisor не равен 1, то инициализируются следующие переменные:
- quotient: переменная, которая будет хранить результат деления.
- sign: переменная, которая будет хранить знак результата деления (-1 или 1).
5. Определяется знак результата деления:
- Если произведение dividend и divisor меньше 0, то знак результата будет отрицательным, и sign устанавливается в -1.
- Иначе знак результата будет положительным, и sign устанавливается в 1.
6. Абсолютные значения dividend и divisor записываются в соответствующие переменные для дальнейших вычислений.
7. Инициализируются переменные i (счетчик) и sum_all (сумма произведений divisor и i).
8. Запускается цикл while, который продолжается, пока sum_all меньше или равно dividend:
- Если dividend - sum_all меньше, чем divisor * i, то i сбрасывается в 1.
- quotient увеличивается на i.
- sum_all увеличивается на divisor * i.
- i увеличивается на 1.
9. После выхода из цикла while возвращается значение (quotient - 1) * sign, что является результатом деления dividend на divisor с учетом знака.
🤔5🔥1
Задача: 30. Substring with Concatenation of All Words #hard
Условие:
Решение:
Пояснение:
1. Импортируется defaultdict из модуля collections. defaultdict - это специальный тип словаря, который автоматически создает новые ключи с значением по умолчанию (в данном случае, 0).
2. Определяется класс Solution с методом findSubstring.
3. Проверяется, если строка s или список words пустые, то возвращается пустой список.
4. Создается defaultdict под названием word_count, в который записываются все слова из списка words и их количество.
5. Вычисляется длина подстроки, которую нужно найти (substr_len) и длина каждого слова (word_len).
6. Создается пустой список result, в который будут записываться индексы начала найденных подстрок.
7. Начинается цикл по всем возможным стартовым индексам подстроки (i от 0 до len(s) - substr_len + 1).
8. Для каждого стартового индекса создается новый defaultdict под названием seen, в который будут записываться найденные слова и их количество.
9. Начинается внутренний цикл, который проходит по словам в подстроке, начиная с индекса i и с шагом word_len.
10. Для каждого слова проверяется, есть ли оно в word_count. Если есть, то оно записывается в seen с увеличением счетчика. Если счетчик в seen превышает количество в word_count, то это означает, что данное слово встречается слишком часто, и мы можем прервать внутренний цикл.
11. Если внутренний цикл завершился без прерывания, то это означает, что найдена подстрока, содержащая все слова из words, и ее стартовый индекс добавляется в result.
12. В конце возвращается список result.
Условие:
Вам дана строка s и массив строк-слов. Все строки-слова имеют одинаковую длину.
Объединенная строка - это строка, которая точно содержит все строки из любой перестановки слов, которые были объединены.
Например, если слова = ["ab", "cd","ef"], то "abcdef", "abefcd", "cdabef", "cdefab", "efabcd" и "efcdab" являются связанными строками. "acdbef" не является объединенной строкой, потому что это не объединение какой-либо перестановки слов.
Возвращает массив начальных индексов всех объединенных подстрок в s. Вы можете вернуть ответ в любом порядке.
Решение:
from collections import defaultdict
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
word_count = defaultdict(int)
for word in words:
word_count[word] += 1
substr_len = len(words) * len(words[0])
word_len = len(words[0])
result = []
for i in range(len(s) - substr_len + 1):
seen = defaultdict(int)
for j in range(i, i + substr_len, word_len):
word = s[j:j+word_len]
if word in word_count:
seen[word] += 1
if seen[word] > word_count[word]:
break
else:
break
else:
result.append(i)
return result
Пояснение:
1. Импортируется defaultdict из модуля collections. defaultdict - это специальный тип словаря, который автоматически создает новые ключи с значением по умолчанию (в данном случае, 0).
2. Определяется класс Solution с методом findSubstring.
3. Проверяется, если строка s или список words пустые, то возвращается пустой список.
4. Создается defaultdict под названием word_count, в который записываются все слова из списка words и их количество.
5. Вычисляется длина подстроки, которую нужно найти (substr_len) и длина каждого слова (word_len).
6. Создается пустой список result, в который будут записываться индексы начала найденных подстрок.
7. Начинается цикл по всем возможным стартовым индексам подстроки (i от 0 до len(s) - substr_len + 1).
8. Для каждого стартового индекса создается новый defaultdict под названием seen, в который будут записываться найденные слова и их количество.
9. Начинается внутренний цикл, который проходит по словам в подстроке, начиная с индекса i и с шагом word_len.
10. Для каждого слова проверяется, есть ли оно в word_count. Если есть, то оно записывается в seen с увеличением счетчика. Если счетчик в seen превышает количество в word_count, то это означает, что данное слово встречается слишком часто, и мы можем прервать внутренний цикл.
11. Если внутренний цикл завершился без прерывания, то это означает, что найдена подстрока, содержащая все слова из words, и ее стартовый индекс добавляется в result.
12. В конце возвращается список result.
👍5❤2🔥1
#medium
Задача: 31. Next Permutation
Перестановка массива целых чисел — это упорядочивание его элементов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.
Для данного массива целых чисел nums найдите следующую перестановку nums.
Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.
Пример:
👨💻 Алгоритм:
1️⃣ Мы меняем местами числа a[i−1] и a[j]. Теперь у нас есть правильное число на индексе i−1. Однако текущая перестановка ещё не является той перестановкой, которую мы ищем. Нам нужна наименьшая перестановка, которая может быть сформирована с использованием чисел только справа от a[i−1]. Следовательно, нам нужно расположить эти числа в порядке возрастания, чтобы получить их наименьшую перестановку.
2️⃣ Однако, вспомните, что, сканируя числа справа налево, мы просто уменьшали индекс, пока не нашли пару a[i] и a[i−1], где a[i] > a[i−1]. Таким образом, все числа справа от a[i−1] уже были отсортированы в порядке убывания. Более того, обмен местами a[i−1] и a[j] не изменил этот порядок.
3️⃣ Поэтому нам просто нужно перевернуть числа, следующие за a[i−1], чтобы получить следующую наименьшую лексикографическую перестановку.
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 31. Next Permutation
Перестановка массива целых чисел — это упорядочивание его элементов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.
Для данного массива целых чисел nums найдите следующую перестановку nums.
Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.
Пример:
Input: nums = [1,2,3]
Output: [1,3,2]
class Solution:
def nextPermutation(self, nums):
i = len(nums) - 2
while i >= 0 and nums[i + 1] <= nums[i]:
i -= 1
if i >= 0:
j = len(nums) - 1
while nums[j] <= nums[i]:
j -= 1
self.swap(nums, i, j)
self.reverse(nums, i + 1)
def reverse(self, nums, start):
i, j = start, len(nums) - 1
while i < j:
self.swap(nums, i, j)
i += 1
j -= 1
def swap(self, nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1🔥1
#hard
Задача: 32. Longest Valid Parentheses
Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.
Пример:
👨💻 Алгоритм:
1️⃣ В этом подходе мы рассматриваем каждую возможную непустую подстроку чётной длины из заданной строки и проверяем, является ли она корректной строкой скобок. Для проверки корректности мы используем метод стека.
2️⃣ Каждый раз, когда мы встречаем символ ‘(’, мы кладём его в стек. Для каждого встреченного символа ‘)’ мы извлекаем из стека символ ‘(’. Если символ ‘(’ недоступен в стеке для извлечения в любой момент или если в стеке остались элементы после обработки всей подстроки, подстрока скобок является некорректной.
3️⃣ Таким образом, мы повторяем процесс для каждой возможной подстроки и продолжаем сохранять длину самой длинной найденной корректной строки.
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 32. Longest Valid Parentheses
Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.
Пример:
Input: s = "(()"
Output: 2
def is_valid(s: str) -> bool:
stack = []
for char in s:
if char == '(':
stack.append('(')
elif stack and stack[-1] == '(':
stack.pop()
else:
return False
return len(stack) == 0
def longest_valid_parentheses(s: str) -> int:
maxlen = 0
for i in range(len(s)):
for j in range(i + 2, len(s) + 1, 2):
substring = s[i:j]
if is_valid(substring):
maxlen = max(maxlen, j - i)
return maxlen
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#medium
Задача: 33. Search in Rotated Sorted Array
Есть массив целых чисел nums, отсортированный в порядке возрастания (с уникальными значениями).
Перед передачей в вашу функцию массив nums может быть повёрнут в неизвестном индексе поворота k (1 <= k < nums.length), так что результирующий массив будет иметь вид [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексацией с нуля). Например, [0,1,2,4,5,6,7] может быть повёрнут в индексе поворота 3 и стать [4,5,6,7,0,1,2].
Для данного массива nums после возможного поворота и целого числа target, верните индекс target, если он есть в массиве, или -1, если его нет в массиве.
Вы должны написать алгоритм с временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Выполните двоичный поиск для определения индекса поворота, инициализируя границы области поиска значениями left = 0 и right = n - 1. Пока left < right:
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.
2️⃣ По завершении двоичного поиска мы имеем индекс поворота, обозначенный как pivot = left.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].
3️⃣ Выполните двоичный поиск по подмассиву nums[0 ~ left - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс.
В противном случае выполните двоичный поиск по подмассиву nums[left ~ n - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс. В противном случае верните -1.
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 33. Search in Rotated Sorted Array
Есть массив целых чисел nums, отсортированный в порядке возрастания (с уникальными значениями).
Перед передачей в вашу функцию массив nums может быть повёрнут в неизвестном индексе поворота k (1 <= k < nums.length), так что результирующий массив будет иметь вид [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексацией с нуля). Например, [0,1,2,4,5,6,7] может быть повёрнут в индексе поворота 3 и стать [4,5,6,7,0,1,2].
Для данного массива nums после возможного поворота и целого числа target, верните индекс target, если он есть в массиве, или -1, если его нет в массиве.
Вы должны написать алгоритм с временной сложностью O(log n).
Пример:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].
В противном случае выполните двоичный поиск по подмассиву nums[left ~ n - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс. В противном случае верните -1.
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
left, right = 0, n - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] > nums[-1]:
left = mid + 1
else:
right = mid - 1
def binarySearch(left_boundary, right_boundary, target):
left, right = left_boundary, right_boundary
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
return -1
if (answer := binarySearch(0, left - 1, target)) != -1:
return answer
return binarySearch(left, n - 1, target)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#medium
Задача: 34. Find First and Last Position of Element in Sorted Array
Дан массив целых чисел nums, отсортированный в неубывающем порядке, найдите начальную и конечную позицию заданного целевого значения.
Если целевое значение не найдено в массиве, верните [-1, -1].
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Определите функцию под названием findBound, которая принимает три аргумента: массив, целевое значение для поиска и булевое значение isFirst, указывающее, ищем ли мы первое или последнее вхождение цели.
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а end — в последний индекс массива.
2️⃣ Мы итерируем, пока begin не станет больше, чем end.
На каждом шаге мы вычисляем средний элемент mid = (begin + end) / 2. Мы используем значение среднего элемента, чтобы решить, какую половину массива нам нужно искать.
Если nums[mid] == target:
Если isFirst true — это означает, что мы пытаемся найти первое вхождение элемента. Если mid == begin или nums[mid - 1] != target, тогда мы возвращаем mid как первое вхождение цели. В противном случае мы обновляем end = mid - 1.
Если isFirst false — это означает, что мы пытаемся найти последнее вхождение элемента. Если mid == end или nums[mid + 1] != target, тогда мы возвращаем mid как последнее вхождение цели. В противном случае мы обновляем begin = mid + 1.
3️⃣ Если nums[mid] > target — мы обновляем end = mid - 1, так как мы должны отбросить правую сторону массива, поскольку средний элемент больше цели.
Если nums[mid] < target — мы обновляем begin = mid + 1, так как мы должны отбросить левую сторону массива, поскольку средний элемент меньше цели.
В конце нашей функции мы возвращаем значение -1, что указывает на то, что цель не найдена в массиве.
В основной функции searchRange мы сначала вызываем findBound с isFirst, установленным в true. Если это значение равно -1, мы можем просто вернуть [-1, -1]. В противном случае мы вызываем findBound с isFirst, установленным в false, чтобы получить последнее вхождение, а затем возвращаем результат.
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 34. Find First and Last Position of Element in Sorted Array
Дан массив целых чисел nums, отсортированный в неубывающем порядке, найдите начальную и конечную позицию заданного целевого значения.
Если целевое значение не найдено в массиве, верните [-1, -1].
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а end — в последний индекс массива.
На каждом шаге мы вычисляем средний элемент mid = (begin + end) / 2. Мы используем значение среднего элемента, чтобы решить, какую половину массива нам нужно искать.
Если nums[mid] == target:
Если isFirst true — это означает, что мы пытаемся найти первое вхождение элемента. Если mid == begin или nums[mid - 1] != target, тогда мы возвращаем mid как первое вхождение цели. В противном случае мы обновляем end = mid - 1.
Если isFirst false — это означает, что мы пытаемся найти последнее вхождение элемента. Если mid == end или nums[mid + 1] != target, тогда мы возвращаем mid как последнее вхождение цели. В противном случае мы обновляем begin = mid + 1.
Если nums[mid] < target — мы обновляем begin = mid + 1, так как мы должны отбросить левую сторону массива, поскольку средний элемент меньше цели.
В конце нашей функции мы возвращаем значение -1, что указывает на то, что цель не найдена в массиве.
В основной функции searchRange мы сначала вызываем findBound с isFirst, установленным в true. Если это значение равно -1, мы можем просто вернуть [-1, -1]. В противном случае мы вызываем findBound с isFirst, установленным в false, чтобы получить последнее вхождение, а затем возвращаем результат.
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
lower_bound = self.findBound(nums, target, True)
if lower_bound == -1:
return [-1, -1]
upper_bound = self.findBound(nums, target, False)
return [lower_bound, upper_bound]
def findBound(self, nums: List[int], target: int, isFirst: bool) -> int:
N = len(nums)
begin, end = 0, N - 1
while begin <= end:
mid = int((begin + end) / 2)
if nums[mid] == target:
if isFirst:
if mid == begin or nums[mid - 1] < target:
return mid
end = mid - 1
else:
if mid == end or nums[mid + 1] > target:
return mid
begin = mid + 1
elif nums[mid] > target:
end = mid - 1
else:
begin = mid + 1
return -1
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#easy
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте указатели left и right: left = 0, right = n - 1.
2️⃣ Пока left <= right:
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
3️⃣ Верните left.
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
Input: nums = [1,3,5,6], target = 5
Output: 2
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
class Solution:
def searchInsert(self, nums, target):
left = 0
right = len(nums) - 1
while left <= right:
pivot = left + (right - left) // 2
if nums[pivot] == target:
return pivot
if target < nums[pivot]:
right = pivot - 1
else:
left = pivot + 1
return left
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3
#medium
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте список из 9 хеш-множеств, где хеш-множество с индексом r будет использоваться для хранения ранее увиденных чисел в строке r судоку. Аналогично инициализируйте списки из 9 хеш-множеств для отслеживания столбцов и блоков.
2️⃣ Итерируйтесь по каждой позиции (r, c) в судоку. На каждой итерации, если на текущей позиции есть число:
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
3️⃣ В противном случае обновите множество, отвечающее за отслеживание ранее увиденных чисел в текущей строке, столбце и блоке. Индекс текущего блока рассчитывается как (r / 3) * 3 + (c / 3), где / означает деление нацело.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
Input: board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
N = 9
rows = [set() for _ in range(N)]
cols = [set() for _ in range(N)]
boxes = [set() for _ in range(N)]
for r in range(N):
for c in range(N):
val = board[r][c]
if val == ".":
continue
if val in rows[r]:
return False
rows[r].add(val)
if val in cols[c]:
return False
cols[c].add(val)
idx = (r // 3) * 3 + c // 3
if val in boxes[idx]:
return False
boxes[idx].add(val)
return True
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4🤔4👍2
#hard
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
👨💻 Алгоритм:
1️⃣ Теперь все готово для написания функции обратного поиска backtrack(row = 0, col = 0). Начните с верхней левой ячейки row = 0, col = 0. Продолжайте, пока не дойдете до первой свободной ячейки.
2️⃣ Итерируйте по числам от 1 до 9 и попробуйте поставить каждое число d в ячейку (row, col).
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
3️⃣ Если вы на последней ячейке row == 8, col == 8:
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).
😎 Решение:
🪙 1096 вопроса вопроса на Python разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
Input: board =
[["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]]
Output:
[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).
from collections import defaultdict
class Solution:
def solveSudoku(self, board):
n = 3
N = n * n
rows, cols, boxes = [defaultdict(int) for _ in range(N)], [defaultdict(int) for _ in range(N)], [defaultdict(int) for _ in range(N)]
def place_or_remove(d, r, c, add=True):
idx = (r // n) * n + (c // n)
rows[r][d] = rows[r].get(d, 0) + (1 if add else -1)
cols[c][d] = cols[c].get(d, 0) + (1 if add else -1)
boxes[idx][d] = boxes[idx].get(d, 0) + (1 if add else -1)
board[r][c] = str(d) if add else '.'
if rows[r][d] == 0: del rows[r][d]
if cols[c][d] == 0: del cols[c][d]
if boxes[idx][d] == 0: del boxes[idx][d]
def can_place(d, r, c):
idx = (r // n) * n + (c // n)
return not (d in rows[r] or d in cols[c] or d in boxes[idx])
def backtrack(r=0, c=0):
if c == N:
r, c = r + 1, 0
if r == N:
return True
if board[r][c] == '.':
for d in map(str, range(1, 10)):
if can_place(d, r, c):
place_or_remove(d, r, c, True)
if backtrack(r, c + 1):
return True
place_or_remove(d, r, c, False)
else:
return backtrack(r, c + 1)
return False
for r in range(N):
for c in range(N):
if board[r][c] != '.':
place_or_remove(board[r][c], r, c, True)
backtrack()
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍3❤1
#medium
Задача: 38. Count and Say
Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:
countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".
Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".
Пример:
👨💻 Алгоритм:
1️⃣ Мы хотим использовать шаблон, который соответствует строкам из одинаковых символов, таких как "4", "7777", "2222222".
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
2️⃣ Мы можем разбить это регулярное выражение на три части:
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
3️⃣ *: этот квалификатор, следующий за ссылкой на группу \1, указывает, что мы хотели бы видеть повторение группы ноль или более раз.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 38. Count and Say
Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:
countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".
Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".
Пример:
Input: n = 4
Output: "1211"
Explanation:
countAndSay(1) = "1"
countAndSay(2) = RLE of "1" = "11"
countAndSay(3) = RLE of "11" = "21"
countAndSay(4) = RLE of "21" = "1211"
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
class Solution:
def countAndSay(self, n: int) -> str:
s = "1"
for _ in range(n - 1):
s = re.sub(
r"(.)\1*", lambda m: str(len(m.group(0))) + m.group(1), s
)
return s
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1🤔1
#medium
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
👨💻 Алгоритм:
1️⃣ Как видно, вышеописанный алгоритм обратного отслеживания разворачивается как обход дерева в глубину (DFS - Depth-First Search), который часто реализуется с помощью рекурсии.
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
2️⃣ Для первого базового случая рекурсивной функции, если remain == 0, то есть мы достигаем желаемой целевой суммы, поэтому мы можем добавить текущую комбинацию в итоговый список.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
3️⃣ Помимо вышеупомянутых двух базовых случаев, мы затем продолжаем исследовать подсписок кандидатов, начиная с [start ... n].
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
class Solution:
def combinationSum(self, candidates, target):
results = []
def backtrack(remain, comb, start):
if remain == 0:
results.append(list(comb))
return
elif remain < 0:
return
for i in range(start, len(candidates)):
comb.append(candidates[i])
backtrack(remain - candidates[i], comb, i)
comb.pop()
backtrack(target, [], 0)
return results
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
#medium
Задача: 40. Combination Sum II
Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.
Каждое число в candidates может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
👨💻 Алгоритм:
1️⃣ Во-первых, мы создаём таблицу счётчиков из предоставленного списка чисел. Затем мы используем эту таблицу счётчиков в процессе обратного поиска, который мы определяем как функцию
2️⃣ При каждом вызове функции обратного поиска мы сначала проверяем, достигли ли мы целевой суммы (то есть
3️⃣ Если осталась сумма для заполнения, мы затем перебираем текущую таблицу счётчиков, чтобы выбрать следующего кандидата. После выбора кандидата мы продолжаем исследование, вызывая функцию
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 40. Combination Sum II
Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.
Каждое число в candidates может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
backtrack(comb, remain, curr, candidate_groups, results). Для сохранения состояния на каждом этапе обратного поиска мы используем несколько параметров в функции:comb: комбинация, которую мы построили на данный момент.remain: оставшаяся сумма, которую нам нужно заполнить, чтобы достичь целевой суммы.curr: курсор, который указывает на текущую группу чисел, используемую из таблицы счётчиков.counter: текущая таблица счётчиков.results: окончательные комбинации, которые достигают целевой суммы.sum(comb) = target), и нужно ли прекратить исследование, потому что сумма текущей комбинации превышает желаемую целевую сумму.backtrack() с обновлёнными состояниями. Более важно, что в конце каждого исследования нам нужно вернуть состояние, которое мы обновили ранее, чтобы начать с чистого листа для следующего исследования. Именно из-за этой операции обратного поиска алгоритм получил своё название.class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
def backtrack(comb, remain, curr, counter, results):
if remain == 0:
results.append(list(comb))
return
elif remain < 0:
return
for next_curr in range(curr, len(counter)):
candidate, freq = counter[next_curr]
if freq <= 0:
continue
comb.append(candidate)
counter[next_curr] = (candidate, freq - 1)
backtrack(comb, remain - candidate, next_curr, counter, results)
counter[next_curr] = (candidate, freq)
comb.pop()
results = []
counter = Counter(candidates)
counter = [(c, counter[c]) for c in counter]
backtrack(
comb=[], remain=target, curr=0, counter=counter, results=results
)
return results
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1🤔1
#hard
Задача: 41. First Missing Positive
Дан неотсортированный массив целых чисел nums. Верните наименьшее положительное целое число, которого нет в массиве nums.
Необходимо реализовать алгоритм, который работает за время O(n) и использует O(1) дополнительной памяти.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализировать переменную n длиной массива nums. Создать массив seen размером n + 1. Отметить элементы в массиве nums как просмотренные в массиве seen.
Для каждого числа num в массиве nums, если num больше 0 и меньше или равно n, установить seen[num] в значение true.
2️⃣ Найти наименьшее недостающее положительное число:
Проитерировать от 1 до n, и если seen[i] не равно true, вернуть i как наименьшее недостающее положительное число.
3️⃣ Если массив seen содержит все элементы от 1 до n, вернуть n + 1 как наименьшее недостающее положительное число.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 41. First Missing Positive
Дан неотсортированный массив целых чисел nums. Верните наименьшее положительное целое число, которого нет в массиве nums.
Необходимо реализовать алгоритм, который работает за время O(n) и использует O(1) дополнительной памяти.
Пример:
Input: nums = [3,4,-1,1]
Output: 2
Explanation: 1 is in the array but 2 is missing.
Для каждого числа num в массиве nums, если num больше 0 и меньше или равно n, установить seen[num] в значение true.
Проитерировать от 1 до n, и если seen[i] не равно true, вернуть i как наименьшее недостающее положительное число.
class Solution:
def firstMissingPositive(self, nums):
n = len(nums)
seen = [False] * (n + 1)
for num in nums:
if 0 < num <= n:
seen[num] = True
for i in range(1, n + 1):
if not seen[i]:
return i
return n + 1
Please open Telegram to view this post
VIEW IN TELEGRAM
💊6❤2👍1
#hard
Задача: 42. Trapping Rain Water
Дано n неотрицательных целых чисел, представляющих карту высот, где ширина каждого столбца равна 1. Вычислите, сколько воды он может удержать после дождя.
Пример:
👨💻 Алгоритм:
1️⃣ Найдите максимальную высоту столбца с левого конца до индекса i в массиве left_max.
2️⃣ Найдите максимальную высоту столбца с правого конца до индекса i в массиве right_max.
3️⃣ Итерируйте по массиву высот height и обновляйте ans: добавьте min(left_max[i], right_max[i]) - height[i] к ans.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 42. Trapping Rain Water
Дано n неотрицательных целых чисел, представляющих карту высот, где ширина каждого столбца равна 1. Вычислите, сколько воды он может удержать после дождя.
Пример:
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
class Solution:
def trap(self, height: List[int]) -> int:
if len(height) == 0:
return 0
ans = 0
size = len(height)
left_max, right_max = [0] * size, [0] * size
left_max[0] = height[0]
for i in range(1, size):
left_max[i] = max(height[i], left_max[i - 1])
right_max[size - 1] = height[size - 1]
for i in range(size - 2, -1, -1):
right_max[i] = max(height[i], right_max[i + 1])
for i in range(1, size - 1):
ans += min(left_max[i], right_max[i]) - height[i]
return ans
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔2
#hard
Задача: 43. Multiply Strings
Даны два неотрицательных целых числа num1 и num2, представленные в виде строк. Верните произведение num1 и num2, также представленное в виде строки.
Примечание: Вы не должны использовать встроенную библиотеку BigInteger или прямо преобразовывать входные данные в целые числа.
Пример:
👨💻 Алгоритм:
1️⃣ Переверните оба числа. Инициализируйте массив ans с (N+M) нулями. Для каждой цифры в secondNumber:
Инициализируйте переменную carry, первоначально равную 0.
Инициализируйте массив (currentResult), который начинается с некоторого количества нулей, основываясь на позиции цифры в secondNumber.
2️⃣ Для каждой цифры в firstNumber:
Умножьте цифру из secondNumber на цифру из firstNumber и добавьте предыдущий carry к умножению.
Возьмите остаток от деления умножения на 10, чтобы получить последнюю цифру.
Добавьте последнюю цифру в массив currentResult.
Разделите умножение на 10, чтобы получить новое значение для carry.
3️⃣ После итерации по каждой цифре в первом числе, если carry не равен нулю, добавьте carry в currentResult.
Добавьте currentResult к ans.
Если последняя цифра в ans равна нулю, перед тем как перевернуть ans, необходимо удалить ноль из ans. В противном случае в финальном ответе будет ведущий ноль.
Переверните ans и верните его.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 43. Multiply Strings
Даны два неотрицательных целых числа num1 и num2, представленные в виде строк. Верните произведение num1 и num2, также представленное в виде строки.
Примечание: Вы не должны использовать встроенную библиотеку BigInteger или прямо преобразовывать входные данные в целые числа.
Пример:
Input: num1 = "2", num2 = "3"
Output: "6"
Инициализируйте переменную carry, первоначально равную 0.
Инициализируйте массив (currentResult), который начинается с некоторого количества нулей, основываясь на позиции цифры в secondNumber.
Умножьте цифру из secondNumber на цифру из firstNumber и добавьте предыдущий carry к умножению.
Возьмите остаток от деления умножения на 10, чтобы получить последнюю цифру.
Добавьте последнюю цифру в массив currentResult.
Разделите умножение на 10, чтобы получить новое значение для carry.
Добавьте currentResult к ans.
Если последняя цифра в ans равна нулю, перед тем как перевернуть ans, необходимо удалить ноль из ans. В противном случае в финальном ответе будет ведущий ноль.
Переверните ans и верните его.
class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
first_number = num1[::-1]
second_number = num2[::-1]
N = len(first_number) + len(second_number)
answer = [0] * N
for index, digit in enumerate(second_number):
answer = self.addStrings(
self.multiplyOneDigit(first_number, digit, index), answer
)
if answer[-1] == 0:
answer.pop()
answer.reverse()
return "".join(str(digit) for digit in answer)
def multiplyOneDigit(self, first_number: str, digit2: str, num_zeros: int):
currentResult = [0] * num_zeros
carry = 0
for digit1 in first_number:
multiplication = int(digit1) * int(digit2) + carry
carry = multiplication // 10
currentResult.append(multiplication % 10)
if carry != 0:
currentResult.append(carry)
return currentResult
def addStrings(self, result: list, answer: list) -> list:
carry = 0
new_answer = []
for digit1, digit2 in zip_longest(result, answer, fillvalue=0):
curr_sum = digit1 + digit2 + carry
carry = curr_sum // 10
new_answer.append(curr_sum % 10)
return new_answer
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6