#easy
Задача: 191. Number of 1 Bits
Напишите функцию, которая принимает бинарное представление положительного целого числа и возвращает количество установленных битов (также известных как вес Хэмминга).
Пример:
👨💻 Алгоритм:
1️⃣ Решение простое: проверяем каждый из 32 битов числа. Если бит равен 1, увеличиваем количество 1-битов на единицу.
2️⃣ Для проверки i-го бита числа используем битовую маску. Начинаем с маски m=1, так как двоичное представление 1 это 0000 0000 0000 0000 0000 0000 0000 0001. Логическое И между любым числом и маской 1 дает нам младший бит этого числа.
3️⃣ Для проверки следующего бита сдвигаем маску влево на один:
0000 0000 0000 0000 0000 0000 0000 0010 и так далее.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 191. Number of 1 Bits
Напишите функцию, которая принимает бинарное представление положительного целого числа и возвращает количество установленных битов (также известных как вес Хэмминга).
Пример:
Input: n = 11
Output: 3
Explanation:
The input binary string 1011 has a total of three set bits.
0000 0000 0000 0000 0000 0000 0000 0010 и так далее.
fun hammingWeight(n: Int): Int {
var bits = 0
var mask = 1
for (i in 0 until 32) {
if ((n and mask) != 0) {
bits++
}
mask = mask shl 1
}
return bits
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 198. House Robber
Вы — профессиональный грабитель, планирующий ограбление домов вдоль улицы. В каждом доме спрятана определённая сумма денег, единственное ограничение, мешающее ограбить каждый из них, заключается в том, что соседние дома оснащены охранными системами, которые автоматически свяжутся с полицией, если в одну и ту же ночь будут взломаны два соседних дома.
Учитывая целочисленный массив nums, представляющий сумму денег в каждом доме, верните максимальную сумму денег, которую вы можете ограбить этой ночью, не подняв тревогу.
Пример:
👨💻 Алгоритм:
1️⃣ Определите функцию robFrom(), которая принимает индекс дома, который грабитель должен осмотреть, и массив nums, необходимый для вычислений. На каждом шаге рекурсивного вызова у грабителя есть два варианта: ограбить текущий дом или нет.
2️⃣ Если грабитель выбирает ограбить текущий дом, он должен пропустить следующий, т.е. вызвать robFrom(i + 2, nums). Ответ будет равен значению robFrom(i + 2, nums) плюс сумма, которую грабитель получит, ограбив текущий дом, т.е. nums[i]. В противном случае он может перейти к следующему дому и вернуть прибыль, которую он получит в подзадаче, т.е. robFrom(i + 1, nums).
3️⃣ Нужно найти, сохранить в кэше и вернуть максимум из этих двух вариантов на каждом шаге. robFrom(0, nums) даст ответ на всю задачу.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 198. House Robber
Вы — профессиональный грабитель, планирующий ограбление домов вдоль улицы. В каждом доме спрятана определённая сумма денег, единственное ограничение, мешающее ограбить каждый из них, заключается в том, что соседние дома оснащены охранными системами, которые автоматически свяжутся с полицией, если в одну и ту же ночь будут взломаны два соседних дома.
Учитывая целочисленный массив nums, представляющий сумму денег в каждом доме, верните максимальную сумму денег, которую вы можете ограбить этой ночью, не подняв тревогу.
Пример:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
class Solution {
private val memo = mutableMapOf<Int, Int>()
fun rob(nums: List<Int>): Int {
memo.clear()
return robFrom(0, nums)
}
private fun robFrom(i: Int, nums: List<Int>): Int {
if (i >= nums.size) {
return 0
}
if (memo.containsKey(i)) {
return memo[i]!!
}
val ans = maxOf(robFrom(i + 1, nums), robFrom(i + 2, nums) + nums[i])
memo[i] = ans
return ans
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 199. Binary Tree Right Side View
Дано корень бинарного дерева, представьте, что вы стоите с правой стороны от него, верните значения узлов, которые вы видите, упорядоченные сверху вниз.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте список правого обзора rightside. Инициализируйте две очереди: одну для текущего уровня и одну для следующего. Добавьте корень в очередь nextLevel.
2️⃣ Пока очередь nextLevel не пуста:
Инициализируйте текущий уровень: currLevel = nextLevel и очистите очередь следующего уровня nextLevel.
Пока очередь текущего уровня не пуста:
Извлеките узел из очереди текущего уровня.
Добавьте сначала левый, а затем правый дочерний узел в очередь nextLevel.
Теперь очередь currLevel пуста, и узел, который у нас есть, является последним и составляет часть правого обзора. Добавьте его в rightside.
3️⃣ Верните rightside.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 199. Binary Tree Right Side View
Дано корень бинарного дерева, представьте, что вы стоите с правой стороны от него, верните значения узлов, которые вы видите, упорядоченные сверху вниз.
Пример:
Input: root = [1,2,3,null,5,null,4]
Output: [1,3,4]
Инициализируйте текущий уровень: currLevel = nextLevel и очистите очередь следующего уровня nextLevel.
Пока очередь текущего уровня не пуста:
Извлеките узел из очереди текущего уровня.
Добавьте сначала левый, а затем правый дочерний узел в очередь nextLevel.
Теперь очередь currLevel пуста, и узел, который у нас есть, является последним и составляет часть правого обзора. Добавьте его в rightside.
class Solution {
fun rightSideView(root: TreeNode?): List<Int> {
if (root == null) return emptyList()
val nextLevel: MutableList<TreeNode> = mutableListOf(root)
val rightside: MutableList<Int> = mutableListOf()
while (nextLevel.isNotEmpty()) {
val currLevel = nextLevel.toMutableList()
nextLevel.clear()
for (node in currLevel) {
node.left?.let { nextLevel.add(it) }
node.right?.let { nextLevel.add(it) }
}
rightside.add(currLevel.last().`val`)
}
return rightside
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
🤯2
#medium
Задача: 200. Number of Islands
Дана двумерная бинарная сетка размером m x n, представляющая карту из '1' (земля) и '0' (вода). Верните количество островов.
Остров окружён водой и образуется путём соединения соседних земель горизонтально или вертикально. Можно предположить, что все четыре края сетки окружены водой.
Пример:
👨💻 Алгоритм:
1️⃣ Линейно просканируйте двумерную карту, если узел содержит '1', то это корневой узел, который запускает поиск в глубину (DFS).
2️⃣ Во время выполнения DFS каждый посещённый узел следует установить в '0', чтобы пометить его как посещённый.
3️⃣ Подсчитайте количество корневых узлов, запускающих DFS. Это количество будет равно количеству островов, так как каждый DFS, начинающийся с какого-либо корня, идентифицирует остров.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 200. Number of Islands
Дана двумерная бинарная сетка размером m x n, представляющая карту из '1' (земля) и '0' (вода). Верните количество островов.
Остров окружён водой и образуется путём соединения соседних земель горизонтально или вертикально. Можно предположить, что все четыре края сетки окружены водой.
Пример:
Input: grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1
class Solution {
fun numIslands(grid: Array<CharArray>): Int {
if (grid.isEmpty()) return 0
var numIslands = 0
for (i in grid.indices) {
for (j in grid[0].indices) {
if (grid[i][j] == '1') {
dfs(grid, i, j)
numIslands++
}
}
}
return numIslands
}
private fun dfs(grid: Array<CharArray>, r: Int, c: Int) {
if (r < 0 || c < 0 || r >= grid.size || c >= grid[0].size || grid[r][c] != '1') {
return
}
grid[r][c] = '0'
dfs(grid, r - 1, c)
dfs(grid, r + 1, c)
dfs(grid, r, c - 1)
dfs(grid, r, c + 1)
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 201. Bitwise AND of Numbers Range
Даны два целых числа left и right, которые представляют диапазон [left, right], верните побитовое И всех чисел в этом диапазоне включительно.
Пример:
👨💻 Алгоритм:
1️⃣ Сдвигать left и right вправо, пока они не станут равными.
2️⃣ Подсчитать количество сдвигов.
3️⃣ Сдвинуть left влево на количество сдвигов и вернуть результат.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 201. Bitwise AND of Numbers Range
Даны два целых числа left и right, которые представляют диапазон [left, right], верните побитовое И всех чисел в этом диапазоне включительно.
Пример:
Input: left = 5, right = 7
Output: 4
class Solution {
fun rangeBitwiseAnd(left: Int, right: Int): Int {
var m = left
var n = right
var shift = 0
while (m < n) {
m = m shr 1
n = n shr 1
shift += 1
}
return m shl shift
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#easy
Задача: 202. Happy Number
Напишите алгоритм для определения, является ли число n счастливым.
Счастливое число определяется следующим процессом:
Начиная с любого положительного целого числа, замените число суммой квадратов его цифр.
Повторяйте процесс, пока число не станет равным 1 (где оно и останется), или пока оно бесконечно не будет циклически повторяться в цикле, который не включает 1.
Те числа, для которых этот процесс завершается 1, являются счастливыми.
Верните true, если n является счастливым числом, и false, если нет.
Пример:
👨💻 Алгоритм:
1️⃣ Для заданного числа n определите следующее число в последовательности: используйте операторы деления и взятия остатка для последовательного извлечения цифр из числа, пока не закончатся все цифры. Каждую извлеченную цифру возводите в квадрат и суммируйте полученные значения. Это техника "последовательного извлечения цифр" является полезным инструментом для решения множества задач.
2️⃣ Отслеживайте цепочку чисел и определяйте, не вошли ли вы в цикл, используя структуру данных HashSet. Каждый раз, генерируя следующее число в цепочке, проверяйте, присутствует ли оно уже в HashSet.
3️⃣ Если числа нет в HashSet, добавьте его туда. Если число уже есть в HashSet, это означает, что вы находитесь в цикле, и следует вернуть false. HashSet используется вместо Vector, List или Array, потому что проверка присутствия числа в HashSet занимает время O(1), тогда как в других структурах данных это займет время O(n). Правильный выбор структур данных является ключевым элементом решения подобных задач.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 202. Happy Number
Напишите алгоритм для определения, является ли число n счастливым.
Счастливое число определяется следующим процессом:
Начиная с любого положительного целого числа, замените число суммой квадратов его цифр.
Повторяйте процесс, пока число не станет равным 1 (где оно и останется), или пока оно бесконечно не будет циклически повторяться в цикле, который не включает 1.
Те числа, для которых этот процесс завершается 1, являются счастливыми.
Верните true, если n является счастливым числом, и false, если нет.
Пример:
Input: n = 2
Output: false
class Solution {
private fun getNext(n: Int): Int {
var n = n
var totalSum = 0
while (n > 0) {
val digit = n % 10
n /= 10
totalSum += digit * digit
}
return totalSum
}
fun isHappy(n: Int): Boolean {
var n = n
val seen = mutableSetOf<Int>()
while (n != 1 && n !in seen) {
seen.add(n)
n = getNext(n)
}
return n == 1
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#easy
Задача: 203. Remove Linked List Elements
Для заданного начала связного списка и целого числа val удалите все узлы связного списка, у которых Node.val равно val, и верните новое начало списка.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте сторожевой узел как ListNode(0) и установите его новым началом: sentinel.next = head. Инициализируйте два указателя для отслеживания текущего узла и его предшественника: curr и prev.
2️⃣ Пока curr не является нулевым указателем, сравните значение текущего узла со значением для удаления. Если значения равны, удалите текущий узел: prev.next = curr.next, иначе установите предшественника равным текущему узлу. Переместитесь к следующему узлу: curr = curr.next.
3️⃣ Верните sentinel.next.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 203. Remove Linked List Elements
Для заданного начала связного списка и целого числа val удалите все узлы связного списка, у которых Node.val равно val, и верните новое начало списка.
Пример:
Input: head = [1,2,6,3,4,5,6], val = 6
Output: [1,2,3,4,5]
class ListNode(var `val`: Int) {
var next: ListNode? = null
}
fun deleteNode(head: ListNode?, value: Int): ListNode? {
val sentinel = ListNode(0)
sentinel.next = head
var prev: ListNode? = sentinel
var curr: ListNode? = head
while (curr != null) {
if (curr.`val` == value) {
prev?.next = curr.next
} else {
prev = curr
}
curr = curr.next
}
return sentinel.next
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 204. Count Primes
Дано целое число n, верните количество простых чисел, которые строго меньше n.
Пример:
👨💻 Алгоритм:
1️⃣ Создайте список последовательных целых чисел от 2 до n: (2, 3, 4, ..., n). Пусть p будет переменной, используемой во внешнем цикле, проходящем от 2 до n. Изначально p равно 2, самому маленькому простому числу.
2️⃣ Перечислите кратные числа p, считая с шагом p от pp до n и отметьте их в списке (это будут pp, pp + p, pp + 2*p и т.д.; само число p должно быть простым). Найдите наименьшее число в списке, большее p, которое не отмечено. Если такого числа нет, остановитесь. В противном случае, пусть p теперь равно этому новому числу (следующее простое) и повторите шаг 2.
3️⃣ Когда алгоритм завершится, все оставшиеся числа, которые не отмечены, являются простыми.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 204. Count Primes
Дано целое число n, верните количество простых чисел, которые строго меньше n.
Пример:
Input: n = 10
Output: 4
Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.
class Solution {
fun countPrimes(n: Int): Int {
if (n <= 2) {
return 0
}
val numbers = BooleanArray(n) { true }
for (p in 2..Math.sqrt(n.toDouble()).toInt()) {
if (numbers[p]) {
for (j in p * p until n step p) {
numbers[j] = false
}
}
}
var numberOfPrimes = 0
for (i in 2 until n) {
if (numbers[i]) {
numberOfPrimes++
}
}
return numberOfPrimes
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#easy
Задача: 205. Isomorphic Strings
Даны две строки s и t, определите, являются ли они изоморфными.
Две строки s и t являются изоморфными, если символы в s могут быть заменены для получения t.
Все вхождения одного символа должны быть заменены другим символом, сохраняя порядок символов. Два символа не могут отображаться в один и тот же символ, но один символ может отображаться сам на себя.
Пример:
👨💻 Алгоритм:
1️⃣ Определите два словаря: mapping_s_t для отображения символов из строки s в символы строки t, и mapping_t_s для отображения символов из строки t в символы строки s.
2️⃣ Итеративно пройдитесь по символам строк s и t. Для каждого символа c1 из s и соответствующего c2 из t, если c1 нет в mapping_s_t и c2 нет в mapping_t_s, добавьте соответствующие отображения; если одно из отображений существует, проверьте, соответствует ли оно ожидаемому значению.
3️⃣ Если в процессе проверки какое-либо отображение неверно, верните false. Если все проверки пройдены успешно, верните true после окончания итерации по строкам.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 205. Isomorphic Strings
Даны две строки s и t, определите, являются ли они изоморфными.
Две строки s и t являются изоморфными, если символы в s могут быть заменены для получения t.
Все вхождения одного символа должны быть заменены другим символом, сохраняя порядок символов. Два символа не могут отображаться в один и тот же символ, но один символ может отображаться сам на себя.
Пример:
Input: s = "egg", t = "add"
Output: true
class Solution {
fun isIsomorphic(s: String, t: String): Boolean {
val mappingStoT = mutableMapOf<Char, Char>()
val mappingTtoS = mutableMapOf<Char, Char>()
for (i in s.indices) {
val c1 = s[i]
val c2 = t[i]
if (!mappingStoT.containsKey(c1) && !mappingTtoS.containsKey(c2)) {
mappingStoT[c1] = c2
mappingTtoS[c2] = c1
} else if (mappingStoT[c1] != c2 || mappingTtoS[c2] != c1) {
return false
}
}
return true
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#easy
Задача: 206. Reverse Linked List
Дан односвязный список, разверните этот список и верните развернутый список.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте две переменные: prev как nullptr и curr как head списка. Эти переменные будут использоваться для отслеживания предыдущего и текущего узлов в процессе разворота списка.
2️⃣ Пройдитесь по списку с помощью цикла:
Сохраните ссылку на следующий узел curr в переменную nextTemp.
Измените ссылку next текущего узла curr на prev, чтобы развернуть направление ссылки.
Переместите prev на текущий узел curr и переместите curr на следующий узел nextTemp.
3️⃣ После завершения цикла верните prev как новую голову развернутого списка.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 206. Reverse Linked List
Дан односвязный список, разверните этот список и верните развернутый список.
Пример:
Input: head = [1,2,3,4,5]
Output: [5,4,3,2,1]
Сохраните ссылку на следующий узел curr в переменную nextTemp.
Измените ссылку next текущего узла curr на prev, чтобы развернуть направление ссылки.
Переместите prev на текущий узел curr и переместите curr на следующий узел nextTemp.
class ListNode(var `val`: Int) {
var next: ListNode? = null
}
class Solution {
fun reverseList(head: ListNode?): ListNode? {
var prev: ListNode? = null
var curr = head
while (curr != null) {
val nextTemp = curr.next
curr.next = prev
prev = curr
curr = nextTemp
}
return prev
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👾 1078 вопросов собесов на Kotlin Developer
🔒 База реальных собесов
🔒 База тестовых заданий
👾 Список менторов
👩💻 Kotlin
├ Вопросы собесов
├ Вакансии
└ Тесты
👩💻 Swift
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
👣 Golang
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
👩💻 С/С++
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
👩💻 PHP
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
🖥 Frontend
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
🖥 Тестировщик
├ Вопросы собесов
├ Вакансии
└ Тесты
🖥 Python
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
👩💻 Java
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
👩💻 С#
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
🖥 Data Science
├ Вопросы собесов
├ Вакансии
└ Тесты
👩💻 DevOps
├ Вопросы собесов
├ Вакансии
└ Тесты
⚙ Backend
└ Вопросы собесов
👾 Список менторов
├ Вопросы собесов
├ Вакансии
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
├ LeetCode ответы
└ Тесты
├ Вопросы собесов
├ Вакансии
└ Тесты
├ Вопросы собесов
├ Вакансии
└ Тесты
└ Вопросы собесов
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
#medium
Задача: 208. Implement Trie (Prefix Tree)
Trie (произносится как "трай") или префиксное дерево — это древовидная структура данных, используемая для эффективного хранения и поиска ключей в наборе строк. Существует множество применений этой структуры данных, таких как автозаполнение и проверка орфографии.
Реализуйте класс Trie:
Trie() инициализирует объект trie.
void insert(String word) вставляет строку word в trie.
boolean search(String word) возвращает true, если строка word есть в trie (то есть была вставлена ранее), и false в противном случае.
boolean startsWith(String prefix) возвращает true, если есть ранее вставленная строка word, которая имеет префикс prefix, и false в противном случае.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализация и вставка в Trie:
Создайте класс Trie, который включает в себя метод insert(String word) для добавления строк в Trie.
Метод insert инициализирует текущий узел как корень и проходит по каждому символу строки. Если текущий узел не содержит символа, создайте новый узел. В конце отметьте последний узел как конец слова.
2️⃣ Поиск строки в Trie:
Создайте метод search(String word), который использует вспомогательный метод searchPrefix(String word) для поиска строки или префикса в Trie.
В методе searchPrefix начните с корневого узла и для каждого символа строки перемещайтесь к следующему узлу. Если на каком-то этапе узел не содержит текущего символа, верните null. В противном случае, в конце строки верните текущий узел.
3️⃣ Проверка наличия префикса в Trie:
Создайте метод startsWith(String prefix), который также использует метод searchPrefix(String prefix).
Метод startsWith вызывает searchPrefix и возвращает true, если возвращаемый узел не равен null, что указывает на наличие префикса в Trie.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 208. Implement Trie (Prefix Tree)
Trie (произносится как "трай") или префиксное дерево — это древовидная структура данных, используемая для эффективного хранения и поиска ключей в наборе строк. Существует множество применений этой структуры данных, таких как автозаполнение и проверка орфографии.
Реализуйте класс Trie:
Trie() инициализирует объект trie.
void insert(String word) вставляет строку word в trie.
boolean search(String word) возвращает true, если строка word есть в trie (то есть была вставлена ранее), и false в противном случае.
boolean startsWith(String prefix) возвращает true, если есть ранее вставленная строка word, которая имеет префикс prefix, и false в противном случае.
Пример:
Input
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output
[null, null, true, false, true, null, true]
Explanation
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // return True
trie.search("app"); // return False
trie.startsWith("app"); // return True
trie.insert("app");
trie.search("app"); // return True
Создайте класс Trie, который включает в себя метод insert(String word) для добавления строк в Trie.
Метод insert инициализирует текущий узел как корень и проходит по каждому символу строки. Если текущий узел не содержит символа, создайте новый узел. В конце отметьте последний узел как конец слова.
Создайте метод search(String word), который использует вспомогательный метод searchPrefix(String word) для поиска строки или префикса в Trie.
В методе searchPrefix начните с корневого узла и для каждого символа строки перемещайтесь к следующему узлу. Если на каком-то этапе узел не содержит текущего символа, верните null. В противном случае, в конце строки верните текущий узел.
Создайте метод startsWith(String prefix), который также использует метод searchPrefix(String prefix).
Метод startsWith вызывает searchPrefix и возвращает true, если возвращаемый узел не равен null, что указывает на наличие префикса в Trie.
class TrieNode {
private val links = arrayOfNulls<TrieNode>(26)
var isEnd = false
private set
fun containsKey(ch: Char): Boolean {
return links[ch - 'a'] != null
}
fun get(ch: Char): TrieNode? {
return links[ch - 'a']
}
fun put(ch: Char, node: TrieNode) {
links[ch - 'a'] = node
}
fun setEnd() {
isEnd = true
}
}
class Trie {
private val root = TrieNode()
fun insert(word: String) {
var node = root
for (ch in word) {
if (!node.containsKey(ch)) {
node.put(ch, TrieNode())
}
node = node.get(ch)!!
}
node.setEnd()
}
private fun searchPrefix(word: String): TrieNode? {
var node = root
for (ch in word) {
if (node.containsKey(ch)) {
node = node.get(ch)!!
} else {
return null
}
}
return node
}
fun search(word: String): Boolean {
val node = searchPrefix(word)
return node != null && node.isEnd
}
fun startsWith(prefix: String): Boolean {
return searchPrefix(prefix) != null
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 209. Minimum Size Subarray Sum
Дан массив положительных целых чисел nums и положительное целое число target. Верните минимальную длину подмассива, сумма которого больше или равна target. Если такого подмассива нет, верните 0.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализация переменных:
Создайте три целочисленные переменные left, right и sumOfCurrentWindow. Переменные left и right формируют подмассив, указывая на начальные и конечные индексы текущего подмассива (или окна), а sumOfCurrentWindow хранит сумму этого окна. Инициализируйте все их значением 0.
Создайте еще одну переменную res для хранения ответа на задачу. Инициализируйте ее большим целым значением.
2️⃣ Итерация по массиву:
Итерируйте по массиву nums с помощью right, начиная с right = 0 до nums.length - 1, увеличивая right на 1 после каждой итерации. Выполняйте следующее внутри этой итерации:
Добавьте элемент с индексом right к текущему окну, увеличив sumOfCurrentWindow на nums[right].
Проверьте, если sumOfCurrentWindow >= target. Если да, у нас есть подмассив, который удовлетворяет условию. В результате, попытайтесь обновить переменную ответа res длиной этого подмассива. Выполните res = min(res, right - left + 1). Затем удалите первый элемент из этого окна, уменьшив sumOfCurrentWindow на nums[left] и увеличив left на 1. Этот шаг повторяется во внутреннем цикле, пока sumOfCurrentWindow >= target.
3️⃣ Возврат результата:
Текущая сумма окна теперь меньше target. Нужно добавить больше элементов в окно. В результате, увеличивается right на 1.
Верните res.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 209. Minimum Size Subarray Sum
Дан массив положительных целых чисел nums и положительное целое число target. Верните минимальную длину подмассива, сумма которого больше или равна target. Если такого подмассива нет, верните 0.
Пример:
Input: target = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: The subarray [4,3] has the minimal length under the problem constraint.
Создайте три целочисленные переменные left, right и sumOfCurrentWindow. Переменные left и right формируют подмассив, указывая на начальные и конечные индексы текущего подмассива (или окна), а sumOfCurrentWindow хранит сумму этого окна. Инициализируйте все их значением 0.
Создайте еще одну переменную res для хранения ответа на задачу. Инициализируйте ее большим целым значением.
Итерируйте по массиву nums с помощью right, начиная с right = 0 до nums.length - 1, увеличивая right на 1 после каждой итерации. Выполняйте следующее внутри этой итерации:
Добавьте элемент с индексом right к текущему окну, увеличив sumOfCurrentWindow на nums[right].
Проверьте, если sumOfCurrentWindow >= target. Если да, у нас есть подмассив, который удовлетворяет условию. В результате, попытайтесь обновить переменную ответа res длиной этого подмассива. Выполните res = min(res, right - left + 1). Затем удалите первый элемент из этого окна, уменьшив sumOfCurrentWindow на nums[left] и увеличив left на 1. Этот шаг повторяется во внутреннем цикле, пока sumOfCurrentWindow >= target.
Текущая сумма окна теперь меньше target. Нужно добавить больше элементов в окно. В результате, увеличивается right на 1.
Верните res.
class Solution {
fun minSubArrayLen(target: Int, nums: IntArray): Int {
var left = 0
var sumOfCurrentWindow = 0
var res = Int.MAX_VALUE
for (right in nums.indices) {
sumOfCurrentWindow += nums[right]
while (sumOfCurrentWindow >= target) {
res = minOf(res, right - left + 1)
sumOfCurrentWindow -= nums[left++]
}
}
return if (res == Int.MAX_VALUE) 0 else res
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 210. Course Schedule II
Всего есть numCourses курсов, которые вы должны пройти, пронумерованных от 0 до numCourses - 1. Вам дан массив prerequisites, где prerequisites[i] = [ai, bi] указывает на то, что вы должны сначала пройти курс bi, если хотите взять курс ai.
Например, пара [0, 1] указывает на то, что для прохождения курса 0 сначала нужно пройти курс 1.
Верните порядок курсов, которые вы должны пройти, чтобы завершить все курсы. Если существует несколько правильных ответов, верните любой из них. Если невозможно завершить все курсы, верните пустой массив.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализация и построение графа:
Инициализируйте стек S, который будет содержать топологически отсортированный порядок курсов в нашем графе.
Постройте список смежности, используя пары ребер, указанные на входе. Важно отметить, что пара вида [a, b] указывает на то, что курс b должен быть пройден, чтобы взять курс a. Это подразумевает ребро вида b ➔ a. Учтите это при реализации алгоритма.
2️⃣ Запуск поиска в глубину (DFS):
Для каждого узла в нашем графе выполните поиск в глубину (DFS), если этот узел еще не был посещен во время DFS другого узла.
Предположим, что мы выполняем поиск в глубину для узла N. Рекурсивно обойдите всех соседей узла N, которые еще не были обработаны.
3️⃣ Обработка узлов и возвращение результата:
После обработки всех соседей добавьте узел N в стек. Мы используем стек для моделирования необходимого порядка. Когда мы добавляем узел N в стек, все узлы, которые требуют узел N в качестве предшественника (среди других), уже будут в стеке.
После обработки всех узлов просто верните узлы в порядке их присутствия в стеке от вершины до основания.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 210. Course Schedule II
Всего есть numCourses курсов, которые вы должны пройти, пронумерованных от 0 до numCourses - 1. Вам дан массив prerequisites, где prerequisites[i] = [ai, bi] указывает на то, что вы должны сначала пройти курс bi, если хотите взять курс ai.
Например, пара [0, 1] указывает на то, что для прохождения курса 0 сначала нужно пройти курс 1.
Верните порядок курсов, которые вы должны пройти, чтобы завершить все курсы. Если существует несколько правильных ответов, верните любой из них. Если невозможно завершить все курсы, верните пустой массив.
Пример:
Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
Output: [0,2,1,3]
Объяснение: Всего есть 4 курса, которые нужно пройти. Чтобы взять курс 3, вы должны завершить оба курса 1 и 2. Оба курса 1 и 2 должны быть взяты после того, как вы завершите курс 0.
Таким образом, один из правильных порядков курсов — [0,1,2,3]. Другой правильный порядок — [0,2,1,3].
Инициализируйте стек S, который будет содержать топологически отсортированный порядок курсов в нашем графе.
Постройте список смежности, используя пары ребер, указанные на входе. Важно отметить, что пара вида [a, b] указывает на то, что курс b должен быть пройден, чтобы взять курс a. Это подразумевает ребро вида b ➔ a. Учтите это при реализации алгоритма.
Для каждого узла в нашем графе выполните поиск в глубину (DFS), если этот узел еще не был посещен во время DFS другого узла.
Предположим, что мы выполняем поиск в глубину для узла N. Рекурсивно обойдите всех соседей узла N, которые еще не были обработаны.
После обработки всех соседей добавьте узел N в стек. Мы используем стек для моделирования необходимого порядка. Когда мы добавляем узел N в стек, все узлы, которые требуют узел N в качестве предшественника (среди других), уже будут в стеке.
После обработки всех узлов просто верните узлы в порядке их присутствия в стеке от вершины до основания.
class Solution {
companion object {
const val WHITE = 1
const val GRAY = 2
const val BLACK = 3
}
private var isPossible: Boolean = true
private lateinit var color: MutableMap<Int, Int>
private val adjList: MutableMap<Int, MutableList<Int>> = HashMap()
private val topologicalOrder: MutableList<Int> = ArrayList()
private fun init(numCourses: Int) {
isPossible = true
color = HashMap()
adjList.clear()
topologicalOrder.clear()
for (i in 0 until numCourses) {
color[i] = WHITE
}
}
private fun dfs(node: Int) {
if (!isPossible) return
color[node] = GRAY
for (neighbor in adjList.getOrDefault(node, mutableListOf())) {
when (color[neighbor]) {
WHITE -> dfs(neighbor)
GRAY -> isPossible = false
}
}
color[node] = BLACK
topologicalOrder.add(node)
}
fun findOrder(numCourses: Int, prerequisites: Array<IntArray>): IntArray {
init(numCourses)
for (prerequisite in prerequisites) {
val (dest, src) = prerequisite
adjList.computeIfAbsent(src) { mutableListOf() }.add(dest)
}
for (i in 0 until numCourses) {
if (color[i] == WHITE) {
dfs(i)
}
}
return if (isPossible) {
val order = IntArray(numCourses)
for (i in 0 until numCourses) {
order[i] = topologicalOrder[numCourses - i - 1]
}
order
} else {
intArrayOf()
}
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
#easy
Задача: 252. Meeting Rooms
Дан массив интервалов времени встреч, где intervals[i] = [starti, endi]. Определите, может ли человек посетить все встречи.
Пример:
👨💻 Алгоритм:
1️⃣ Создайте функцию для проверки перекрытия двух интервалов:
Возвращайте true, если начало одного интервала находится внутри другого интервала.
2️⃣ Проверьте каждый интервал с каждым другим интервалом:
Если найдено перекрытие, верните false.
3️⃣ Если все интервалы проверены и перекрытий не найдено, верните true.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 252. Meeting Rooms
Дан массив интервалов времени встреч, где intervals[i] = [starti, endi]. Определите, может ли человек посетить все встречи.
Пример:
Input: intervals = [[0,30],[5,10],[15,20]]
Output: false
Возвращайте true, если начало одного интервала находится внутри другого интервала.
Если найдено перекрытие, верните false.
class Solution {
fun canAttendMeetings(intervals: List<List<Int>>): Boolean {
fun overlap(interval1: List<Int>, interval2: List<Int>): Boolean {
return (interval1[0] >= interval2[0] && interval1[0] < interval2[1]) ||
(interval2[0] >= interval1[0] && interval2[0] < interval1[1])
}
for (i in intervals.indices) {
for (j in i + 1 until intervals.size) {
if (overlap(intervals[i], intervals[j])) {
return false
}
}
}
return true
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 253. Meeting Rooms II
Дан массив интервалов времени встреч intervals, где intervals[i] = [starti, endi]. Верните минимальное количество необходимых конференц-залов.
Пример:
👨💻 Алгоритм:
1️⃣ Отсортируйте встречи по времени их начала и инициализируйте мин-кучу с временем окончания первой встречи.
2️⃣ Для каждой последующей встречи проверьте, свободна ли комната (сравните время начала встречи с минимальным временем окончания в куче):
Если свободна, обновите время окончания этой комнаты.
Если не свободна, добавьте новое время окончания в кучу.
3️⃣ После обработки всех встреч размер кучи будет равен минимальному количеству необходимых комнат.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 253. Meeting Rooms II
Дан массив интервалов времени встреч intervals, где intervals[i] = [starti, endi]. Верните минимальное количество необходимых конференц-залов.
Пример:
Input: intervals = [[0,30],[5,10],[15,20]]
Output: 2
Если свободна, обновите время окончания этой комнаты.
Если не свободна, добавьте новое время окончания в кучу.
import java.util.*
class Solution {
fun minMeetingRooms(intervals: Array<IntArray>): Int {
intervals.sortBy { it[0] }
val heap = PriorityQueue<Int>()
heap.add(intervals[0][1])
for (i in 1 until intervals.size) {
if (intervals[i][0] >= heap.peek()) {
heap.poll()
}
heap.add(intervals[i][1])
}
return heap.size
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 254. Factor Combinations
Числа можно рассматривать как произведение их множителей.
Например, 8 = 2 x 2 x 2 = 2 x 4.
Дано целое число n, верните все возможные комбинации его множителей. Вы можете вернуть ответ в любом порядке.
Обратите внимание, что множители должны быть в диапазоне [2, n - 1].
Пример:
👨💻 Алгоритм:
1️⃣ Определите вспомогательную функцию backtracking, которая принимает два параметра: factors (список множителей) и ans (список списков для сохранения всех комбинаций множителей). Начните вызов backtracking с factors, содержащим только n, и пустым списком ans.
2️⃣ Основная логика функции backtracking:
Если размер factors больше 1, добавьте его копию в ans, так как это одно из желаемых решений.
Получите последний элемент factors (lastFactor) и удалите его из factors.
Если factors пуст, итерируйте i от 2. В противном случае, итерируйте i от последнего значения в factors. Итерируйте, пока i <= lastFactor / i.
Для каждого i, если lastFactor % i == 0, добавьте i и lastFactor / i в factors и вызовите backtracking(factors, ans).
Восстановите список (откат) factors, удалив последние два элемента из factors.
Восстановите список (откат) factors, добавив обратно lastFactor.
3️⃣ В конце выполнения, ans будет содержать все возможные комбинации множителей числа n.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 254. Factor Combinations
Числа можно рассматривать как произведение их множителей.
Например, 8 = 2 x 2 x 2 = 2 x 4.
Дано целое число n, верните все возможные комбинации его множителей. Вы можете вернуть ответ в любом порядке.
Обратите внимание, что множители должны быть в диапазоне [2, n - 1].
Пример:
Input: n = 1
Output: []
Если размер factors больше 1, добавьте его копию в ans, так как это одно из желаемых решений.
Получите последний элемент factors (lastFactor) и удалите его из factors.
Если factors пуст, итерируйте i от 2. В противном случае, итерируйте i от последнего значения в factors. Итерируйте, пока i <= lastFactor / i.
Для каждого i, если lastFactor % i == 0, добавьте i и lastFactor / i в factors и вызовите backtracking(factors, ans).
Восстановите список (откат) factors, удалив последние два элемента из factors.
Восстановите список (откат) factors, добавив обратно lastFactor.
class Solution {
private fun backtracking(factors: MutableList<Int>, ans: MutableList<List<Int>>) {
if (factors.size > 1) {
ans.add(ArrayList(factors))
}
val lastFactor = factors.removeAt(factors.size - 1)
for (i in (if (factors.isEmpty()) 2 else factors.last())..lastFactor) {
if (i * i > lastFactor) break
if (lastFactor % i == 0) {
factors.add(i)
factors.add(lastFactor / i)
backtracking(factors, ans)
factors.removeAt(factors.size - 1)
factors.removeAt(factors.size - 1)
}
}
factors.add(lastFactor)
}
fun getFactors(n: Int): List<List<Int>> {
val ans = mutableListOf<List<Int>>()
backtracking(mutableListOf(n), ans)
return ans
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 255. Verify Preorder Sequence in Binary Search Tree
Дан массив уникальных целых чисел preorder. Верните true, если это правильная последовательность обхода в порядке предварительного (preorder) обхода для бинарного дерева поиска.
Пример:
👨💻 Алгоритм:
1️⃣ Объявите целое число minLimit с маленьким значением, например, минус бесконечность, и стек.
2️⃣ Итерируйте по массиву preorder. Для каждого num:
Очистите стек. Пока верх стека меньше num, извлекайте из стека и обновляйте minLimit.
Если num <= minLimit, верните false.
Добавьте num в стек.
3️⃣ Верните true, если удалось пройти весь массив.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 255. Verify Preorder Sequence in Binary Search Tree
Дан массив уникальных целых чисел preorder. Верните true, если это правильная последовательность обхода в порядке предварительного (preorder) обхода для бинарного дерева поиска.
Пример:
Input: preorder = [5,2,1,3,6]
Output: true
Очистите стек. Пока верх стека меньше num, извлекайте из стека и обновляйте minLimit.
Если num <= minLimit, верните false.
Добавьте num в стек.
class Solution {
fun verifyPreorder(preorder: IntArray): Boolean {
var minLimit = Int.MIN_VALUE
val stack = mutableListOf<Int>()
for (num in preorder) {
while (stack.isNotEmpty() && stack.last() < num) {
minLimit = stack.removeAt(stack.size - 1)
}
if (num <= minLimit) {
return false
}
stack.add(num)
}
return true
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 211. Design Add and Search Words Data Structure
Спроектируйте структуру данных, которая поддерживает добавление новых слов и проверку, соответствует ли строка любому ранее добавленному слову.
Реализуйте класс WordDictionary:
WordDictionary() инициализирует объект.
void addWord(word) добавляет слово в структуру данных, оно может быть сопоставлено позже.
bool search(word) возвращает true, если в структуре данных есть строка, которая соответствует слову, или false в противном случае. Слово может содержать точки '.', где точки могут быть сопоставлены с любой буквой.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализация и добавление слова:
Создайте класс WordDictionary с конструктором, который инициализирует корневой узел TrieNode.
Метод addWord(String word) добавляет слово в структуру данных. Инициализируйте текущий узел как корневой и пройдите по каждому символу слова. Если символ отсутствует среди дочерних узлов текущего узла, создайте новый узел. Перемещайтесь к следующему узлу. В конце отметьте текущий узел как конец слова.
2️⃣ Поиск слова в узле:
Метод searchInNode(String word, TrieNode node) ищет слово в переданном узле TrieNode. Пройдите по каждому символу слова. Если символ не найден среди дочерних узлов текущего узла, проверьте, является ли символ точкой '.'. Если да, рекурсивно выполните поиск в каждом дочернем узле текущего узла. Если символ не точка и не найден, верните false. Если символ найден, перейдите к следующему узлу. В конце проверьте, является ли текущий узел концом слова.
3️⃣ Поиск слова в структуре данных:
Метод search(String word) использует метод searchInNode() для поиска слова, начиная с корневого узла. Верните результат поиска. Если слово найдено, верните true, иначе false.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 211. Design Add and Search Words Data Structure
Спроектируйте структуру данных, которая поддерживает добавление новых слов и проверку, соответствует ли строка любому ранее добавленному слову.
Реализуйте класс WordDictionary:
WordDictionary() инициализирует объект.
void addWord(word) добавляет слово в структуру данных, оно может быть сопоставлено позже.
bool search(word) возвращает true, если в структуре данных есть строка, которая соответствует слову, или false в противном случае. Слово может содержать точки '.', где точки могут быть сопоставлены с любой буквой.
Пример:
Input
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
Output
[null,null,null,null,false,true,true,true]
Explanation
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True
Создайте класс WordDictionary с конструктором, который инициализирует корневой узел TrieNode.
Метод addWord(String word) добавляет слово в структуру данных. Инициализируйте текущий узел как корневой и пройдите по каждому символу слова. Если символ отсутствует среди дочерних узлов текущего узла, создайте новый узел. Перемещайтесь к следующему узлу. В конце отметьте текущий узел как конец слова.
Метод searchInNode(String word, TrieNode node) ищет слово в переданном узле TrieNode. Пройдите по каждому символу слова. Если символ не найден среди дочерних узлов текущего узла, проверьте, является ли символ точкой '.'. Если да, рекурсивно выполните поиск в каждом дочернем узле текущего узла. Если символ не точка и не найден, верните false. Если символ найден, перейдите к следующему узлу. В конце проверьте, является ли текущий узел концом слова.
Метод search(String word) использует метод searchInNode() для поиска слова, начиная с корневого узла. Верните результат поиска. Если слово найдено, верните true, иначе false.
class TrieNode {
val children: MutableMap<Char, TrieNode> = HashMap()
var isWord: Boolean = false
}
class WordDictionary {
private val root: TrieNode = TrieNode()
fun addWord(word: String) {
var node = root
for (ch in word) {
node = node.children.computeIfAbsent(ch) { TrieNode() }
}
node.isWord = true
}
private fun searchInNode(word: String, node: TrieNode): Boolean {
var node = node
for (i in word.indices) {
val ch = word[i]
if (!node.children.containsKey(ch)) {
if (ch == '.') {
for (child in node.children.values) {
if (searchInNode(word.substring(i + 1), child)) {
return true
}
}
}
return false
} else {
node = node.children[ch]!!
}
}
return node.isWord
}
fun search(word: String): Boolean {
return searchInNode(word, root)
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
#hard
Задача: 212. Word Search II
Дана m на n доска символов и список строк words, верните все слова, находящиеся на доске.
Каждое слово должно быть составлено из букв последовательных смежных ячеек, где смежные ячейки находятся по горизонтали или вертикали рядом. Одна и та же ячейка с буквой не может использоваться более одного раза в слове.
Пример:
👨💻 Алгоритм:
1️⃣ Построение Trie:
Постройте структуру Trie из слов в словаре. Trie будет использоваться для процесса сопоставления позже.
2️⃣ Запуск обхода в глубину (Backtracking) с каждой ячейки:
Начните обход доски с каждой ячейки. Если существует слово в словаре, которое начинается с буквы в данной ячейке, начните рекурсивный вызов функции backtracking(cell).
3️⃣ Обход соседних ячеек:
В функции backtracking(cell) исследуйте соседние ячейки (i.e. neighborCell) вокруг текущей ячейки для следующего рекурсивного вызова backtracking(neighborCell).
На каждом вызове проверяйте, соответствует ли последовательность букв, которую мы прошли до сих пор, какому-либо слову в словаре, используя структуру Trie, построенную в начале.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 212. Word Search II
Дана m на n доска символов и список строк words, верните все слова, находящиеся на доске.
Каждое слово должно быть составлено из букв последовательных смежных ячеек, где смежные ячейки находятся по горизонтали или вертикали рядом. Одна и та же ячейка с буквой не может использоваться более одного раза в слове.
Пример:
Input: board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
Output: ["eat","oath"]
Постройте структуру Trie из слов в словаре. Trie будет использоваться для процесса сопоставления позже.
Начните обход доски с каждой ячейки. Если существует слово в словаре, которое начинается с буквы в данной ячейке, начните рекурсивный вызов функции backtracking(cell).
В функции backtracking(cell) исследуйте соседние ячейки (i.e. neighborCell) вокруг текущей ячейки для следующего рекурсивного вызова backtracking(neighborCell).
На каждом вызове проверяйте, соответствует ли последовательность букв, которую мы прошли до сих пор, какому-либо слову в словаре, используя структуру Trie, построенную в начале.
class TrieNode {
val children = HashMap<Char, TrieNode>()
var word: String? = null
}
class Solution {
private lateinit var board: Array<CharArray>
private val result = ArrayList<String>()
fun findWords(board: Array<CharArray>, words: Array<String>): List<String> {
val root = TrieNode()
for (word in words) {
var node = root
for (letter in word) {
node = node.children.computeIfAbsent(letter) { TrieNode() }
}
node.word = word
}
this.board = board
for (row in board.indices) {
for (col in board[0].indices) {
if (root.children.containsKey(board[row][col])) {
backtracking(row, col, root)
}
}
}
return result
}
private fun backtracking(row: Int, col: Int, parent: TrieNode) {
val letter = board[row][col]
val currNode = parent.children[letter] ?: return
currNode.word?.let {
result.add(it)
currNode.word = null
}
board[row][col] = '#'
val rowOffset = intArrayOf(-1, 0, 1, 0)
val colOffset = intArrayOf(0, 1, 0, -1)
for (i in 0 until 4) {
val newRow = row + rowOffset[i]
val newCol = col + colOffset[i]
if (newRow in board.indices && newCol in board[0].indices) {
if (currNode.children.containsKey(board[newRow][newCol])) {
backtracking(newRow, newCol, currNode)
}
}
}
board[row][col] = letter
if (currNode.children.isEmpty()) {
parent.children.remove(letter)
}
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 213. House Robber II
Вы профессиональный грабитель, планирующий ограбить дома вдоль улицы. В каждом доме спрятано определенное количество денег. Все дома в этом месте расположены по кругу, что означает, что первый дом является соседом последнего. Между тем, в соседних домах установлена охранная система, которая автоматически свяжется с полицией, если два соседних дома будут взломаны в одну ночь.
Дан массив целых чисел nums, представляющий количество денег в каждом доме, верните максимальную сумму денег, которую вы можете ограбить этой ночью, не вызвав полицию.
Пример:
👨💻 Алгоритм:
1️⃣ Обработка базовых случаев:
Если массив nums пуст, возвращаем 0.
Если в массиве nums только один дом, возвращаем значение этого дома.
2️⃣ Разделение задачи на две подзадачи:
Находим максимальную сумму для подмассива домов от первого до предпоследнего, вызывая функцию robSimple с параметрами 0 и nums.size - 2.
Находим максимальную сумму для подмассива домов от второго до последнего, вызывая функцию robSimple с параметрами 1 и nums.size - 1.
3️⃣ Сравнение результатов и возврат максимального значения:
Возвращаем максимальное значение из двух полученных результатов.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 213. House Robber II
Вы профессиональный грабитель, планирующий ограбить дома вдоль улицы. В каждом доме спрятано определенное количество денег. Все дома в этом месте расположены по кругу, что означает, что первый дом является соседом последнего. Между тем, в соседних домах установлена охранная система, которая автоматически свяжется с полицией, если два соседних дома будут взломаны в одну ночь.
Дан массив целых чисел nums, представляющий количество денег в каждом доме, верните максимальную сумму денег, которую вы можете ограбить этой ночью, не вызвав полицию.
Пример:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
Если массив nums пуст, возвращаем 0.
Если в массиве nums только один дом, возвращаем значение этого дома.
Находим максимальную сумму для подмассива домов от первого до предпоследнего, вызывая функцию robSimple с параметрами 0 и nums.size - 2.
Находим максимальную сумму для подмассива домов от второго до последнего, вызывая функцию robSimple с параметрами 1 и nums.size - 1.
Возвращаем максимальное значение из двух полученных результатов.
class Solution {
fun rob(nums: IntArray): Int {
if (nums.isEmpty()) return 0
if (nums.size == 1) return nums[0]
val max1 = robSimple(nums, 0, nums.size - 2)
val max2 = robSimple(nums, 1, nums.size - 1)
return maxOf(max1, max2)
}
private fun robSimple(nums: IntArray, start: Int, end: Int): Int {
var t1 = 0
var t2 = 0
for (i in start..end) {
val temp = t1
val current = nums[i]
t1 = maxOf(current + t2, t1)
t2 = temp
}
return t1
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1