Python | LeetCode
10.1K subscribers
151 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
#easy
Задача: 94. Binary Tree Inorder Traversal

Дан корень бинарного дерева. Верните обход значений его узлов в симметричном порядке.

Пример:
Input: root = [1,null,2,3]
Output: [1,3,2]


👨‍💻 Алгоритм:

Метод решения этой задачи использует рекурсию. Это классический метод, и он простой. Мы можем определить вспомогательную функцию для реализации рекурсии.

😎 Решение:
class Solution:
def inorderTraversal(self, root):
res = []
self.helper(root, res)
return res

def helper(self, root, res):
if root is not None:
self.helper(root.left, res)
res.append(root.val)
self.helper(root.right, res)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#medium
Задача: 95. Unique Binary Search Trees II

Дано целое число n. Верните все структурно уникальные деревья бинарного поиска (BST), которые содержат ровно n узлов с уникальными значениями от 1 до n. Ответ может быть представлен в любом порядке.

Пример:
Input: n = 3
Output: [[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]


👨‍💻 Алгоритм:

1️⃣Создайте хеш-карту memo, где memo[(start, end)] содержит список корневых узлов всех возможных BST (деревьев бинарного поиска) с диапазоном значений узлов от start до end. Реализуем рекурсивную функцию allPossibleBST, которая принимает начальный диапазон значений узлов start, конечный диапазон end и memo в качестве параметров. Она возвращает список TreeNode, соответствующих всем BST, которые могут быть сформированы с этим диапазоном значений узлов. Вызываем allPossibleBST(1, n, memo) и выполняем следующее:

2️⃣Объявляем список TreeNode под названием res для хранения списка корневых узлов всех возможных BST. Если start > end, мы добавляем null в res и возвращаем его. Если мы уже решили эту подзадачу, т.е. memo содержит пару (start, end), мы возвращаем memo[(start, end)].

3️⃣Выбираем значение корневого узла от i = start до end, увеличивая i на 1 после каждой итерации. Рекурсивно вызываем leftSubtrees = allPossibleBST(start, i - 1, memo) и rightSubTrees = allPossibleBST(i + 1, end, memo). Перебираем все пары между leftSubtrees и rightSubTrees и создаем новый корень со значением i для каждой пары. Добавляем корень новообразованного BST в res. Устанавливаем memo[(start, end)] = res и возвращаем res.

😎 Решение:
class Solution:
def allPossibleBST(self, start, end, memo):
res = []
if start > end:
res.append(None)
return res
if (start, end) in memo:
return memo[(start, end)]

for i in range(start, end + 1):
leftSubTrees = self.allPossibleBST(start, i - 1, memo)
rightSubTrees = self.allPossibleBST(i + 1, end, memo)
for left in leftSubTrees:
for right in rightSubTrees:
root = TreeNode(i, left, right)
res.append(root)

memo[(start, end)] = res
return res

def generateTrees(self, n: int) -> List[Optional[TreeNode]]:
memo = {}
return self.allPossibleBST(1, n, memo)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
#medium
Задача: 96. Unique Binary Search Trees

Дано целое число n. Верните количество структурно уникальных деревьев бинарного поиска (BST), которые содержат ровно n узлов с уникальными значениями от 1 до n.

Пример:
Input: n = 3
Output: 5


👨‍💻 Алгоритм:

1️⃣Задача состоит в том, чтобы рассчитать количество уникальных BST (бинарных деревьев поиска). Для этого мы можем определить две функции:

G(n): количество уникальных BST для последовательности длины n.

F(i, n): количество уникальных BST, где число i является корнем BST (1 ≤ i ≤ n).

Как видно, G(n) - это именно та функция, которая нам нужна для решения задачи.

2️⃣Позднее мы увидим, что G(n) можно вывести из F(i, n), которая, в свою очередь, рекурсивно относится к G(n).

Следуя идее из раздела "Интуиция", мы видим, что общее количество уникальных BST G(n) равно сумме BST F(i, n) с перечислением каждого числа i (1 ≤ i ≤ n) в качестве корня. Таким образом,

G(n) = ∑ F(i, n) для i от 1 до n.

В частности, для базовых случаев есть только одна комбинация для построения BST из последовательности длиной 1 (только корень) или ничего (пустое дерево). То есть G(0) = 1, G(1) = 1.

3️⃣Дана последовательность от 1 до n, мы выбираем число i из последовательности в качестве корня, тогда количество уникальных BST с указанным корнем, определенным как F(i, n), является декартовым произведением количества BST для его левого и правого поддеревьев, как показано ниже:

Например, F(3,7) - количество уникальных деревьев BST с числом 3 в качестве корня. Для построения уникального BST из всей последовательности [1, 2, 3, 4, 5, 6, 7] с 3 в качестве корня, мы должны построить поддерево из его левой подпоследовательности [1, 2] и другое поддерево из правой подпоследовательности [4, 5, 6, 7], а затем соединить их вместе (то есть декартово произведение). Теперь хитрость заключается в том, что мы можем рассматривать количество уникальных BST из последовательности [1,2] как G(2), и количество уникальных BST из последовательности [4, 5, 6, 7] как G(4). Для G(n) не имеет значения содержание последовательности, а только длина последовательности. Следовательно, F(3,7) = G(2)⋅G(4). Обобщая пример, мы можем вывести следующую формулу:

F(i, n) = G(i−1)⋅G(n−i)

Объединяя формулы (1), (2), мы получаем рекурсивную формулу для G(n), то есть

G(n) = ∑ G(i−1)⋅G(n−i) для i от 1 до n.

Чтобы вычислить результат функции, мы начинаем с меньшего числа, так как значение G(n) зависит от значений G(0)...G(n−1).

😎 Решение:
class Solution:
def numTrees(self, n: int) -> int:
G = [0] * (n + 1)
G[0], G[1] = 1, 1

for i in range(2, n + 1):
for j in range(1, i + 1):
G[i] += G[j - 1] * G[i - j]

return G[n]


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 97. Interleaving String

Даны строки s1, s2 и s3. Необходимо определить, может ли строка s3 быть сформирована путем чередования строк s1 и s2.

Чередование двух строк s и t — это конфигурация, при которой s и t делятся на n и m подстрок соответственно так, что:

s = s1 + s2 + ... + sn
t = t1 + t2 + ... + tm
|n - m| ≤ 1
Чередование может быть таким: s1 + t1 + s2 + t2 + s3 + t3 + ... или t1 + s1 + t2 + s2 + t3 + s3 + ...
Примечание: a + b означает конкатенацию строк a и b.

Пример:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
Output: true
Explanation: One way to obtain s3 is:
Split s1 into s1 = "aa" + "bc" + "c", and s2 into s2 = "dbbc" + "a".
Interleaving the two splits, we get "aa" + "dbbc" + "bc" + "a" + "c" = "aadbbcbcac".


👨‍💻 Алгоритм:

1️⃣В рекурсивном подходе, описанном выше, рассматривается каждая возможная строка, образованная путем чередования двух заданных строк. Однако возникают случаи, когда та же часть s1 и s2 уже была обработана, но в разных порядках (перестановках). Независимо от порядка обработки, если результирующая строка до этого момента совпадает с требуемой строкой (s3), окончательный результат зависит только от оставшихся частей s1 и s2, а не от уже обработанной части. Таким образом, рекурсивный подход приводит к избыточным вычислениям.

2️⃣Эту избыточность можно устранить, используя мемоизацию вместе с рекурсией. Для этого мы поддерживаем три указателя i, j, k, которые соответствуют индексу текущего символа s1, s2, s3 соответственно. Также мы поддерживаем двумерный массив memo для отслеживания обработанных до сих пор подстрок. memo[i][j] хранит 1/0 или -1 в зависимости от того, была ли текущая часть строк, то есть до i-го индекса для s1 и до j-го индекса для s2, уже оценена. Мы начинаем с выбора текущего символа s1 (на который указывает i). Если он совпадает с текущим символом s3 (на который указывает k), мы включаем его в обработанную строку и вызываем ту же функцию рекурсивно как: is_Interleave(s1, i+1, s2, j, s3, k+1, memo).

3️⃣Таким образом, мы вызвали функцию, увеличив указатели i и k, поскольку часть строк до этих индексов уже была обработана. Аналогично, мы выбираем один символ из второй строки и продолжаем. Рекурсивная функция заканчивается, когда одна из двух строк s1 или s2 полностью обработана. Если, скажем, строка s1 полностью обработана, мы напрямую сравниваем оставшуюся часть s2 с оставшейся частью s3. Когда происходит возврат из рекурсивных вызовов, мы сохраняем значение, возвращенное рекурсивными функциями, в массиве мемоизации memo соответственно, так что когда оно встречается в следующий раз, рекурсивная функция не будет вызвана, но сам массив мемоизации вернет предыдущий сгенерированный результат.

😎 Решение:
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
if len(s3) != len(s1) + len(s2):
return False
dp = [[False] * (len(s2) + 1) for _ in range(len(s1) + 1)]
for i in range(len(s1) + 1):
for j in range(len(s2) + 1):
if i == 0 and j == 0:
dp[i][j] = True
elif i == 0:
dp[i][j] = dp[i][j - 1] and s2[j - 1] == s3[i + j - 1]
elif j == 0:
dp[i][j] = dp[i - 1][j] and s1[i - 1] == s3[i + j - 1]
else:
dp[i][j] = (
dp[i - 1][j] and s1[i - 1] == s3[i + j - 1]
) or (dp[i][j - 1] and s2[j - 1] == s3[i + j - 1])
return dp[len(s1)][len(s2)]


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
#medium
Задача: 98. Validate Binary Search Tree

Дан корень бинарного дерева. Определите, является ли это дерево допустимым бинарным деревом поиска (BST).

Допустимое BST определяется следующим образом:

Левое поддерево узла содержит только узлы с ключами, меньшими, чем ключ узла.
Правое поддерево узла содержит только узлы с ключами, большими, чем ключ узла.
Оба поддерева — левое и правое — также должны быть бинарными деревьями поиска.

Пример:
Input: root = [2,1,3]
Output: true


👨‍💻 Алгоритм:

1️⃣Давайте воспользуемся порядком узлов при симметричном обходе (inorder traversal):

Левый -> Узел -> Правый.

Постордер:

Здесь узлы перечисляются в порядке их посещения, и вы можете следовать последовательности 1-2-3-4-5 для сравнения различных стратегий.

Порядок "Левый -> Узел -> Правый" при симметричном обходе означает, что для BST каждый элемент должен быть меньше следующего.

2️⃣Следовательно, алгоритм с временной сложностью O(N) и пространственной сложностью O(N) может быть простым:

Вычислить список симметричного обхода inorder.

Проверить, меньше ли каждый элемент в списке inorder следующего за ним.

Постордер:

3️⃣Нужно ли сохранять весь список симметричного обхода?

На самом деле, нет. Достаточно последнего добавленного элемента inorder, чтобы на каждом шаге убедиться, что дерево является BST (или нет). Следовательно, можно объединить оба шага в один и уменьшить используемое пространство.

😎 Решение:
class Solution:
def isValidBST(self, root: TreeNode) -> bool:

def inorder(root):
if not root:
return True
if not inorder(root.left):
return False
if root.val <= self.prev:
return False
self.prev = root.val
return inorder(root.right)

self.prev = -math.inf
return inorder(root)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#medium
Задача: 99. Recover Binary Search Tree

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

Пример:
Input: root = [1,3,null,null,2]
Output: [3,1,null,null,2]
Explanation: 3 cannot be a left child of 1 because 3 > 1. Swapping 1 and 3 makes the BST valid.


👨‍💻 Алгоритм:

1️⃣Создайте симметричный обход дерева. Это должен быть почти отсортированный список, в котором поменяны местами только два элемента.

2️⃣Определите два поменянных местами элемента x и y в почти отсортированном массиве за линейное время.

3️⃣Повторно пройдите по дереву. Измените значение x на y и значение y на x.

😎 Решение:
class Solution:
def recoverTree(self, root: TreeNode) -> None:
def inorder(r: TreeNode) -> List[int]:
return inorder(r.left) + [r.val] + inorder(r.right) if r else []

def find_two_swapped(nums: List[int]) -> (int, int):
n = len(nums)
x = y = (
None
)

for i in range(n - 1):
if nums[i + 1] < nums[i]:
y = nums[i + 1]
if x is None:
x = nums[i]
else:
break
return x, y

def recover(r: TreeNode, count: int) -> None:
if r:
if r.val == x or r.val == y:
r.val = y if r.val == x else x
count -= 1
if count == 0:
return
recover(r.left, count)
recover(r.right, count)

nums = inorder(root)
x, y = find_two_swapped(nums)
recover(root, 2)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
#easy
Задача: 100. Same Tree

Даны корни двух бинарных деревьев p и q. Напишите функцию, чтобы проверить, одинаковы ли они.

Два бинарных дерева считаются одинаковыми, если они структурно идентичны, и узлы имеют одинаковые значения.

Пример:
Input: p = [1,2,3], q = [1,2,3]
Output: true


👨‍💻 Алгоритм:

Самая простая стратегия здесь — использовать рекурсию. Проверьте, не равны ли узлы p и q значению None, и равны ли их значения. Если все проверки пройдены успешно, проделайте то же самое для дочерних узлов рекурсивно.

😎 Решение:
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q:
return True
if not q or not p:
return False
if p.val != q.val:
return False
return self.isSameTree(p.right, q.right) and self.isSameTree(
p.left, q.left
)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
#easy
Задача: 101. Symmetric Tree

Дан корень бинарного дерева. Проверьте, является ли это дерево зеркальным отражением самого себя (то есть симметричным относительно своего центра).

Пример:
Input: root = [1,2,2,3,4,4,3]
Output: true


👨‍💻 Алгоритм:

1️⃣Дерево симметрично, если левое поддерево является зеркальным отражением правого поддерева.

2️⃣Следовательно, вопрос заключается в том, когда два дерева являются зеркальным отражением друг друга?

Два дерева являются зеркальным отражением друг друга, если:

- Их корни имеют одинаковое значение.
- Правое поддерево каждого дерева является зеркальным отражением левого поддерева другого дерева.

3️⃣Это похоже на человека, смотрящего в зеркало. Отражение в зеркале имеет ту же голову, но правая рука отражения соответствует левой руке настоящего человека и наоборот.

Вышеописанное объяснение естественным образом превращается в рекурсивную функцию.

😎 Решение:
class Solution:
def isSymmetric(self, root):
return self.isMirror(root, root)

def isMirror(self, t1, t2):
if t1 is None and t2 is None:
return True
if t1 is None or t2 is None:
return False
return (
(t1.val == t2.val)
and self.isMirror(t1.right, t2.left)
and self.isMirror(t1.left, t2.right)
)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
#medium
Задача: 102. Binary Tree Level Order Traversal

Дан корень бинарного дерева. Верните обход узлов дерева по уровням (то есть слева направо, уровень за уровнем).

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]


👨‍💻 Алгоритм:

1️⃣Самый простой способ решения задачи — использование рекурсии. Сначала убедимся, что дерево не пустое, а затем рекурсивно вызовем функцию helper(node, level), которая принимает текущий узел и его уровень в качестве аргументов.

2️⃣Эта функция выполняет следующее:

Выходной список здесь называется levels, и, таким образом, текущий уровень — это просто длина этого списка len(levels). Сравниваем номер текущего уровня len(levels) с уровнем узла level. Если вы все еще на предыдущем уровне, добавьте новый, добавив новый список в levels.

3️⃣Добавьте значение узла в последний список в levels.

Рекурсивно обработайте дочерние узлы, если они не равны None: helper(node.left / node.right, level + 1).

😎 Решение:
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
levels = []
if not root:
return levels

def helper(node: TreeNode, level: int) -> None:
if len(levels) == level:
levels.append([])

levels[level].append(node.val)

if node.left:
helper(node.left, level + 1)
if node.right:
helper(node.right, level + 1)

helper(root, 0)
return levels


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
#medium
Задача: 103. Binary Tree Zigzag Level Order Traversal

Дан корень бинарного дерева. Верните обход узлов дерева по уровням в виде зигзага (то есть слева направо, затем справа налево для следующего уровня и чередуйте далее).

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: [[3],[20,9],[15,7]]


👨‍💻 Алгоритм:

1️⃣Мы также можем реализовать поиск в ширину (BFS) с использованием одного цикла. Трюк заключается в том, что мы добавляем узлы для посещения в очередь, а узлы разных уровней разделяем с помощью какого-то разделителя (например, пустого узла). Разделитель отмечает конец уровня, а также начало нового уровня.

2️⃣Здесь мы принимаем второй подход, описанный выше. Можно начать с обычного алгоритма BFS, к которому мы добавляем элемент порядка зигзага с помощью deque (двусторонней очереди).

3️⃣Для каждого уровня мы начинаем с пустого контейнера deque, который будет содержать все значения данного уровня. В зависимости от порядка каждого уровня, т.е. либо слева направо, либо справа налево, мы решаем, с какого конца deque добавлять новый элемент.

😎 Решение:
from collections import deque

class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
ret = []
level_list = deque()
if root is None:
return []
node_queue = deque([root, None])
is_order_left = True

while len(node_queue) > 0:
curr_node = node_queue.popleft()

if curr_node:
if is_order_left:
level_list.append(curr_node.val)
else:
level_list.appendleft(curr_node.val)

if curr_node.left:
node_queue.append(curr_node.left)
if curr_node.right:
node_queue.append(curr_node.right)
else:
ret.append(level_list)
if len(node_queue) > 0:
node_queue.append(None)

level_list = deque()
is_order_left = not is_order_left

return ret


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#easy
Задача: 104. Maximum Depth of Binary Tree

Дан корень бинарного дерева, верните его максимальную глубину.

Максимальная глубина бинарного дерева — это количество узлов вдоль самого длинного пути от корневого узла до самого удалённого листового узла.

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: 3


👨‍💻 Алгоритм:

1️⃣Можно обойти дерево, используя стратегию поиска в глубину (DFS) или поиска в ширину (BFS).

2️⃣Для данной задачи подойдет несколько способов.

3️⃣Здесь мы демонстрируем решение, реализованное с использованием стратегии DFS и рекурсии.

😎 Решение:
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
else:
left_height = self.maxDepth(root.left)
right_height = self.maxDepth(root.right)
return max(left_height, right_height) + 1


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
#medium
Задача: 105. Construct Binary Tree from Preorder and Inorder Traversal

Даны два массива целых чисел: preorder и inorder, где preorder — это результат преордер обхода бинарного дерева, а inorder — результат инордер обхода того же дерева. Постройте и верните бинарное дерево.

Пример:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]


👨‍💻 Алгоритм:

1️⃣Создайте хеш-таблицу для записи соотношения значений и их индексов в массиве inorder, чтобы можно было быстро найти позицию корня.

2️⃣Инициализируйте переменную целочисленного типа preorderIndex для отслеживания элемента, который будет использоваться для создания корня. Реализуйте рекурсивную функцию arrayToTree, которая принимает диапазон массива inorder и возвращает построенное бинарное дерево:
Если диапазон пуст, возвращается null;
Инициализируйте корень элементом preorder[preorderIndex], затем увеличьте preorderIndex;
Рекурсивно используйте левую и правую части массива inorder для построения левого и правого поддеревьев.

3️⃣Просто вызовите функцию рекурсии с полным диапазоном массива inorder.

😎 Решение:
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:

def array_to_tree(left, right):
nonlocal preorder_index
if left > right:
return None

root_value = preorder[preorder_index]
root = TreeNode(root_value)

preorder_index += 1

root.left = array_to_tree(left, inorder_index_map[root_value] - 1)
root.right = array_to_tree(inorder_index_map[root_value] + 1, right)

return root

preorder_index = 0
inorder_index_map = {}
for index, value in enumerate(inorder):
inorder_index_map[value] = index

return array_to_tree(0, len(preorder) - 1)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
#medium
Задача: 106. Construct Binary Tree from Inorder and Postorder Traversal

Даны два массива целых чисел: inorder и postorder, где inorder — это массив обхода в глубину бинарного дерева слева направо, а postorder — это массив обхода в глубину после обработки всех потомков узла. Постройте и верните соответствующее бинарное дерево.

Пример:
Input: inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
Output: [3,9,20,null,null,15,7]


👨‍💻 Алгоритм:

1️⃣Создайте хэш-таблицу (hashmap) для хранения соответствия значений и их индексов в массиве обхода inorder.

2️⃣Определите вспомогательную функцию helper, которая принимает границы левой и правой части текущего поддерева в массиве inorder. Эти границы используются для проверки, пусто ли поддерево. Если левая граница больше правой (in_left > in_right), то поддерево пустое и функция возвращает None.

3️⃣Выберите последний элемент в массиве обхода postorder в качестве корня. Значение корня имеет индекс index в обходе inorder. Элементы от in_left до index - 1 принадлежат левому поддереву, а элементы от index + 1 до in_right — правому поддереву. Согласно логике обхода postorder, сначала рекурсивно строится правое поддерево helper(index + 1, in_right), а затем левое поддерево helper(in_left, index - 1). Возвращается корень.

😎 Решение:
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
def helper(in_left: int, in_right: int) -> TreeNode:
if in_left > in_right:
return None

val = postorder.pop()
root = TreeNode(val)

index = idx_map[val]

root.right = helper(index + 1, in_right)
root.left = helper(in_left, index - 1)
return root

idx_map = {val: idx for idx, val in enumerate(inorder)}
return helper(0, len(inorder) - 1)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Forwarded from Идущий к IT
10$ за техническое собеседование на английском языке:

1. Отправьте запись технического собеседования на английском языке файлом на этот аккаунт
2. Добавьте ссылку на вакансию или пришлите название компании и должность
3. Напишите номер кошелка USDT (Tether) на который отправить 10$

🛡 Важно:

– Запись будет использована только для сбора данных о вопросах
– Вы останетесь анонимны
– Запись нигде не будет опубликована

🤝 Условия:

– Внятный звук, различимая речь
– Допустимые профессии:
• Любые программисты
• DevOps
• Тестировщики
• Дата сайнтисты
• Бизнес/Системные аналитики
• Прожекты/Продукты
• UX/UI и продукт дизайнеры
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#medium
Задача: 107. Binary Tree Level Order Traversal II

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

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: [[15,7],[9,20],[3]]


👨‍💻 Алгоритм:

1️⃣Инициализируйте список вывода levels. Длина этого списка определяет, какой уровень в данный момент обновляется. Вам следует сравнить этот уровень len(levels) с уровнем узла level, чтобы убедиться, что вы добавляете узел на правильный уровень. Если вы все еще находитесь на предыдущем уровне, добавьте новый уровень, вставив новый список в levels.

2️⃣Добавьте значение узла в последний уровень в levels.

3️⃣Рекурсивно обработайте дочерние узлы, если они не равны None, используя функцию helper(node.left / node.right, level + 1).

😎 Решение:
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
levels = []
if not root:
return levels

def helper(node: Optional[TreeNode], level: int) -> None:
if len(levels) == level:
levels.append([])

levels[level].append(node.val)

if node.left:
helper(node.left, level + 1)
if node.right:
helper(node.right, level + 1)

helper(root, 0)
return levels[::-1]


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#easy
Задача: 108. Convert Sorted Array to Binary Search Tree

Дан массив целых чисел nums, элементы которого отсортированы в порядке возрастания. Преобразуйте его в сбалансированное по высоте двоичное дерево поиска.

Пример:
Input: nums = [-10,-3,0,5,9]
Output: [0,-3,9,-10,null,5]
Explanation: [0,-10,5,null,-3,null,9] is also accepted:


👨‍💻 Алгоритм:

1️⃣Инициализация функции помощника: Реализуйте функцию помощника helper(left, right), которая строит двоичное дерево поиска (BST) из элементов массива nums между индексами left и right.
Если left > right, это означает, что элементов для построения поддерева нет, возвращаем None.

2️⃣Выбор корня и разделение массива:
Выберите элемент в середине для корня: p = (left + right) // 2.
Инициализируйте корень: root = TreeNode(nums[p]).

3️⃣Рекурсивное построение поддеревьев:
Рекурсивно стройте левое поддерево: root.left = helper(left, p - 1).
Рекурсивно стройте правое поддерево: root.right = helper(p + 1, right).
В качестве результата возвращайте helper(0, len(nums) - 1), начиная с корня дерева.

😎 Решение:
class Solution:
def sortedArrayToBST(self, nums):
def helper(left, right):
if left > right:
return None

p = (left + right) // 2
root = TreeNode(nums[p])
root.left = helper(left, p - 1)
root.right = helper(p + 1, right)
return root

return helper(0, len(nums) - 1)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 109. Convert Sorted List to Binary Search Tree

Дана голова односвязного списка, элементы которого отсортированы в порядке возрастания. Преобразуйте его в сбалансированное по высоте бинарное дерево поиска.

Пример:
Input: head = [-10,-3,0,5,9]
Output: [0,-3,9,-10,null,5]
Explanation: One possible answer is [0,-3,9,-10,null,5], which represents the shown height balanced BST.


👨‍💻 Алгоритм:

1️⃣Поскольку нам дан односвязный список, а не массив, у нас нет прямого доступа к элементам списка по индексам. Нам нужно определить средний элемент односвязного списка. Мы можем использовать подход с двумя указателями для нахождения среднего элемента списка. В основном, у нас есть два указателя: slow_ptr и fast_ptr. slow_ptr перемещается на один узел за раз, тогда как fast_ptr перемещается на два узла за раз. К тому времени, как fast_ptr достигнет конца списка, slow_ptr окажется в середине списка. Для списка с четным количеством элементов любой из двух средних элементов может стать корнем BST.

2️⃣Как только мы находим средний элемент списка, мы отсоединяем часть списка слева от среднего элемента. Мы делаем это, сохраняя prev_ptr, который указывает на узел перед slow_ptr, т.е. prev_ptr.next = slow_ptr. Для отсоединения левой части мы просто делаем prev_ptr.next = None.

3️⃣Для создания сбалансированного по высоте BST нам нужно передать только голову связанного списка в функцию, которая преобразует его в BST. Таким образом, мы рекурсивно работаем с левой половиной списка, передавая оригинальную голову списка, и с правой половиной, передавая slow_ptr.next в качестве головы.

😎 Решение:
class Solution:

def findMiddle(self, head: ListNode) -> ListNode:
prevPtr = None
slowPtr = head
fastPtr = head
while fastPtr and fastPtr.next:
prevPtr = slowPtr
slowPtr = slowPtr.next
fastPtr = fastPtr.next.next
if prevPtr:
prevPtr.next = None
return slowPtr

def sortedListToBST(self, head: ListNode) -> TreeNode:
if not head:
return None
mid = self.findMiddle(head)
node = TreeNode(mid.val)
if head == mid:
return node
node.left = self.sortedListToBST(head)
node.right = self.sortedListToBST(mid.next)
return node


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍2
#easy
Задача: 110. Balanced Binary Tree

Дано бинарное дерево, определите, является ли оно
сбалансированным по высоте.

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: true


👨‍💻 Алгоритм:

1️⃣Сначала мы определяем функцию height, которая для любого узла p в дереве T возвращает:
-1, если p является пустым поддеревом, т.е. null;
1 + max(height(p.left), height(p.right)) в противном случае.

2️⃣Теперь, когда у нас есть метод для определения высоты дерева, остается только сравнить высоты детей каждого узла. Дерево T с корнем r является сбалансированным тогда и только тогда, когда высоты его двух детей отличаются не более чем на 1 и поддеревья каждого ребенка также сбалансированы.

3️⃣Следовательно, мы можем сравнить высоты двух дочерних поддеревьев, а затем рекурсивно проверить каждое из них:
Если root == NULL, возвращаем true.
Если abs(height(root.left) - height(root.right)) > 1, возвращаем false.
В противном случае возвращаем isBalanced(root.left) && isBalanced(root.right).

😎 Решение:
class Solution:
def height(self, root: TreeNode) -> int:
if not root:
return -1
return 1 + max(self.height(root.left), self.height(root.right))

def isBalanced(self, root: TreeNode) -> bool:
if not root:
return True
return (
abs(self.height(root.left) - self.height(root.right)) < 2
and self.isBalanced(root.left)
and self.isBalanced(root.right)
)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#easy
Задача: 111. Minimum Depth of Binary Tree

Дано бинарное дерево, найдите его минимальную глубину.

Минимальная глубина - это количество узлов вдоль самого короткого пути от корневого узла до ближайшего листового узла.

Пример:
Input: root = [3,9,20,null,null,15,7]
Output: 2


👨‍💻 Алгоритм:

1️⃣Мы будем использовать метод обхода в глубину (dfs) с корнем в качестве аргумента.
Базовое условие рекурсии: это для узла NULL, в этом случае мы должны вернуть 0.

2️⃣Если левый ребенок корня является NULL: тогда мы должны вернуть 1 + минимальную глубину для правого ребенка корневого узла, что равно 1 + dfs(root.right).

3️⃣Если правый ребенок корня является NULL: тогда мы должны вернуть 1 + минимальную глубину для левого ребенка корневого узла, что равно 1 + dfs(root.left). Если оба ребенка не являются NULL, тогда вернуть 1 + min(dfs(root.left), dfs(root.right)).

😎 Решение:
class Solution:
def minDepth(self, root):
def dfs(root):
if root is None:
return 0
if root.left is None:
return 1 + dfs(root.right)
elif root.right is None:
return 1 + dfs(root.left)
return 1 + min(dfs(root.left), dfs(root.right))

return dfs(root)


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4👀1
#easy
Задача: 112. Path Sum

Дан корень бинарного дерева и целое число targetSum. Верните true, если в дереве существует путь от корня до листа, такой, что сумма всех значений вдоль пути равна targetSum.

Лист — это узел без детей.

Пример:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
Output: true
Explanation: The root-to-leaf path with the target sum is shown.


👨‍💻 Алгоритм:

1️⃣Инициализация стека: Начать с помещения в стек корневого узла и соответствующей оставшейся суммы, равной sum - root.val.

2️⃣Обработка узлов: Извлечь текущий узел из стека и вернуть True, если оставшаяся сумма равна 0 и узел является листом.

3️⃣Добавление дочерних узлов в стек: Если оставшаяся сумма не равна нулю или узел не является листом, добавить в стек дочерние узлы с соответствующими оставшимися суммами.

😎 Решение:
class Solution:
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if not root:
return False

de = [
(root, sum - root.val),
]
while de:
node, curr_sum = de.pop()
if not node.left and not node.right and curr_sum == 0:
return True
if node.right:
de.append((node.right, curr_sum - node.right.val))
if node.left:
de.append((node.left, curr_sum - node.left.val))
return False


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2