Kotlin | LeetCode
1.84K subscribers
171 photos
1 video
1.11K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+Gzg9SH2MNxM0ZTYy
Вопросы соебсов t.iss.one/+OOb6zFa_-Oo3NjZi
Вакансии t.iss.one/+KuGNaHeKkQg1NzAy
Download Telegram
Задача: 537. Complex Number Multiplication
Сложность: medium

Комплексное число можно представить в виде строки в формате "real+imaginaryi", где:
real — это действительная часть и является целым числом в диапазоне [-100, 100].
imaginary — это мнимая часть и является целым числом в диапазоне [-100, 100].
i^2 == -1.
Даны два комплексных числа num1 и num2 в виде строк, верните строку комплексного числа, представляющую их произведение.

Пример:
Input: num1 = "1+1i", num2 = "1+1i"
Output: "0+2i"
Explanation: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i, and you need convert it to the form of 0+2i.


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

1⃣ Извлечение реальной и мнимой частей:
Разделите строки a и b на реальные и мнимые части, используя символы '+' и 'i'.

2⃣ Вычисление произведения:
Переведите извлечённые части в целые числа.
Используйте формулу для умножения комплексных чисел: (a+ib)×(x+iy)=ax−by+i(bx+ay).

3⃣ Формирование строки результата:
Создайте строку в требуемом формате с реальной и мнимой частями произведения и верните её.

😎 Решение:
class Solution {
fun complexNumberMultiply(a: String, b: String): String {
val x = a.split("+", "i")
val y = b.split("+", "i")
val a_real = x[0].toInt()
val a_img = x[1].toInt()
val b_real = y[0].toInt()
val b_img = y[1].toInt()
val realPart = a_real * b_real - a_img * b_img
val imaginaryPart = a_real * b_img + a_img * b_real
return "$realPart+$imaginaryPart" + "i"
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1000. Minimum Cost to Merge Stones
Сложность: hard

Имеется n кучек камней, расположенных в ряд. В i-й куче находятся камни stones[i]. Ход состоит в объединении ровно k последовательных куч в одну кучу, и стоимость этого хода равна общему количеству камней в этих k кучах. Верните минимальную стоимость объединения всех куч камней в одну кучу. Если это невозможно, верните -1.

Пример:
Input: stones = [3,2,4,1], k = 2
Output: 20


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

1⃣Проверка на возможность объединения:
Проверьте, можно ли объединить все кучи в одну, если количество куч n не равно 1 по модулю k-1. Если нет, верните -1.

2⃣Инициализация и динамическое программирование:
Создайте таблицу dp для хранения минимальных затрат на объединение подмассивов камней.
Используйте таблицу prefix для хранения префиксных сумм камней, чтобы быстро вычислять сумму камней в подмассиве.

3⃣Заполнение таблицы dp:
Заполните таблицу dp минимальными затратами на объединение подмассивов камней, используя динамическое программирование.
Для каждого подмассива длиной от k до n, найдите минимальную стоимость его объединения.

😎 Решение:
class Solution {
fun mergeStones(stones: IntArray, k: Int): Int {
val n = stones.size
if ((n - 1) % (k - 1) != 0) return -1

val prefix = IntArray(n + 1)
for (i in stones.indices) {
prefix[i + 1] = prefix[i] + stones[i]
}

val dp = Array(n) { IntArray(n) }
for (m in k..n) {
for (i in 0..n - m) {
val j = i + m - 1
dp[i][j] = Int.MAX_VALUE
for (t in i until j step k - 1) {
dp[i][j] = minOf(dp[i][j], dp[i][t] + dp[t + 1][j])
}
if ((j - i) % (k - 1) == 0) {
dp[i][j] += prefix[j + 1] - prefix[i]
}
}
}

return dp[0][n - 1]
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1216. Valid Palindrome III
Сложность: hard

Дана строка s и целое число k. Верните true, если s является k-палиндромом.
Строка является k-палиндромом, если её можно преобразовать в палиндром, удалив из неё не более k символов.

Пример:
Input: s = "abcdeca", k = 2
Output: true
Explanation: Remove 'b' and 'e' characters.


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

1⃣Инициализируйте двухмерный массив memo для хранения промежуточных результатов, чтобы избежать повторных вычислений. Определите функцию isValidPalindrome, которая будет возвращать минимальное количество удалений для создания палиндрома в подстроке от индекса i до j.

2⃣Реализуйте базовые случаи для функции isValidPalindrome: если i равно j, то это уже палиндром, если i и j - соседние индексы, то возвращается 1, если символы не совпадают. Если значение для пары индексов уже рассчитано, то возвращается сохраненное значение из memo.

3⃣Реализуйте основные случаи рекурсивного вычисления: если символы на позициях i и j совпадают, продолжайте проверку для подстроки без этих символов. В противном случае, рассмотрите два варианта удаления символов и выберите минимальное количество удалений, добавив 1 за текущее удаление.

😎 Решение:
class Solution {
private lateinit var memo: Array<IntArray?>

fun isValidPalindrome(s: String, k: Int): Boolean {
val n = s.length
memo = Array(n) { IntArray(n) { -1 } }
return isValidPalindromeHelper(s.toCharArray(), 0, n - 1) <= k
}

private fun isValidPalindromeHelper(s: CharArray, i: Int, j: Int): Int {
if (i == j) return 0
if (i == j - 1) return if (s[i] != s[j]) 1 else 0
if (memo[i][j] != -1) return memo[i][j]
memo[i][j] = if (s[i] == s[j]) {
isValidPalindromeHelper(s, i + 1, j - 1)
} else {
1 + minOf(isValidPalindromeHelper(s, i + 1, j), isValidPalindromeHelper(s, i, j - 1))
}
return memo[i][j]
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1208. Get Equal Substrings Within Budget
Сложность: medium

Вам даны две строки s и t одинаковой длины и целое число maxCost.
Вы хотите преобразовать s в t. Изменение i-го символа строки s на i-й символ строки t стоит |s[i] - t[i]| (т.е. абсолютная разница между значениями ASCII символов).

Верните максимальную длину подстроки s, которую можно изменить, чтобы она соответствовала соответствующей подстроке t с затратами, не превышающими maxCost. Если нет подстроки из s, которую можно изменить на соответствующую подстроку из t, верните 0.

Пример:
Input: s = "abcd", t = "bcdf", maxCost = 3
Output: 3
Explanation: "abc" of s can change to "bcd".
That costs 3, so the maximum length is 3.


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

1⃣Инициализация переменных:
maxLen для хранения максимальной длины подстроки с затратами, не превышающими maxCost.
start для хранения начального индекса текущей подстроки.
currCost для хранения текущих затрат на преобразование подстроки s в t.

2⃣Итерация по индексам от 0 до N-1:
Добавить текущие затраты на преобразование символа s[i] в t[i] к currCost.
Удалять элементы с левого конца, уменьшая затраты до тех пор, пока currCost не станет меньше или равным maxCost.
Обновить maxLen длиной текущей подстроки.

3⃣Возврат maxLen как результата.

😎 Решение:
class Solution {
fun equalSubstring(s: String, t: String, maxCost: Int): Int {
val N = s.length

var maxLen = 0
var start = 0
var currCost = 0

for (i in 0 until N) {
currCost += kotlin.math.abs(s[i] - t[i])

while (currCost > maxCost) {
currCost -= kotlin.math.abs(s[start] - t[start])
start++
}

maxLen = maxOf(maxLen, i - start + 1)
}

return maxLen
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 663. Equal Tree Partition
Сложность: medium

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

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


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

1⃣Вычисление общей суммы:
Напишите функцию для вычисления общей суммы всех узлов дерева.

2⃣Проверка возможности разделения:
Напишите функцию, чтобы рекурсивно проверить, может ли поддерево быть равно половине общей суммы. Если такое поддерево найдено, значит дерево можно разделить на две части с равными суммами.

3⃣Валидация и возврат результата:
Проверьте, что общая сумма четная (так как только в этом случае возможно её разделение на две равные части), и используйте функцию проверки поддерева, чтобы определить, можно ли разделить дерево на две части с равными суммами.

😎 Решение:
class TreeNode(var `val`: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}

class Solution {
fun checkEqualTree(root: TreeNode?): Boolean {
if (root == null) return false
val totalSum = sumTree(root)
if (totalSum % 2 != 0) return false
val target = totalSum / 2
return checkSubtreeSum(root, target, root)
}

private fun sumTree(node: TreeNode?): Int {
if (node == null) return 0
return node.`val` + sumTree(node.left) + sumTree(node.right)
}

private fun checkSubtreeSum(node: TreeNode?, target: Int, root: TreeNode): Boolean {
if (node == null) return false
if (node !== root && sumTree(node) == target) {
return true
}
return checkSubtreeSum(node.left, target, root) || checkSubtreeSum(node.right, target, root)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 39. Combination Sum
Сложность: medium

Дан массив candidates уникальных целых чисел и целевое число target.
Верните все уникальные комбинации, сумма которых равна target.
Одно число можно использовать неограниченное количество раз.

Пример:
  
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]


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

1⃣Используем backtracking, начиная с пустой комбинации и target.

2⃣Если target == 0, добавляем комбинацию в результат.

3⃣Если target < 0, отменяем текущий путь, иначе продолжаем рекурсию, начиная с текущего индекса.

😎 Решение:
class Solution {
fun combinationSum(candidates: IntArray, target: Int): List<List<Int>> {
val results = mutableListOf<List<Int>>()

fun backtrack(remain: Int, comb: MutableList<Int>, start: Int) {
when {
remain == 0 -> results.add(ArrayList(comb))
remain > 0 -> {
for (i in start until candidates.size) {
comb.add(candidates[i])
backtrack(remain - candidates[i], comb, i)
comb.removeAt(comb.size - 1)
}
}
}
}

backtrack(target, mutableListOf(), 0)
return results
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 927. Three Equal Parts
Сложность: hard

Вам дан массив arr, состоящий только из нулей и единиц. Разделите массив на три непустые части так, чтобы все эти части представляли одно и то же двоичное значение. Если это возможно, верните любой [i, j] с i + 1 < j, такой что: arr[0], arr[1], ..., arr[i] - это первая часть, arr[i + 1], arr[i + 2], ...., arr[j - 1] - вторая часть, и arr[j], arr[j + 1], ..., arr[arr.length - 1] - третья часть. Все три части имеют одинаковые двоичные значения. Если это невозможно, верните [-1, -1]. Обратите внимание, что вся часть используется при рассмотрении того, какое двоичное значение она представляет. Например, [1,1,0] представляет 6 в десятичной системе, а не 3. Кроме того, допускаются ведущие нули, поэтому [0,1,1] и [1,1] представляют одно и то же значение.

Пример:
Input: arr = [1,0,1,0,1]
Output: [0,3]


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

1⃣Подсчитать количество единиц в массиве.

2⃣Если количество единиц не делится на три, вернуть [-1, -1].
Найти индексы начала каждой части, игнорируя ведущие нули.
Использовать эти индексы для проверки, могут ли три части быть одинаковыми.

3⃣Если три части одинаковы, вернуть соответствующие индексы, иначе вернуть [-1, -1].

😎 Решение:
class Solution {
fun threeEqualParts(arr: IntArray): IntArray {
val ones = arr.sum()
if (ones % 3 != 0) return intArrayOf(-1, -1)
if (ones == 0) return intArrayOf(0, arr.size - 1)

val partOnes = ones / 3
var first = 0
var second = 0
var third = 0
var cnt = 0
for (i in arr.indices) {
if (arr[i] == 1) {
when (cnt) {
0 -> first = i
partOnes -> second = i
2 * partOnes -> third = i
}
cnt++
}
}

while (third < arr.size && arr[first] == arr[second] && arr[first] == arr[third]) {
first++
second++
third++
}

return if (third == arr.size) intArrayOf(first - 1, second) else intArrayOf(-1, -1)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 66. Plus One
Сложность: easy

Вам дано большое число, представленное в виде массива целых чисел digits, где каждый элемент digits[i] — это i-я цифра числа. Цифры расположены в порядке от старшей к младшей слева направо. Большое число не содержит ведущих нулей.

Увеличьте большое число на один и верните результирующий массив цифр.

Пример:
Input: digits = [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123.
Incrementing by one gives 123 + 1 = 124.
Thus, the result should be [1,2,4].


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

1⃣Проходим по входному массиву, начиная с конца массива.

2⃣Устанавливаем все девятки на конце массива в ноль. Если мы встречаем цифру, не равную девяти, увеличиваем её на один. Работа выполнена — возвращаем массив цифр.

3⃣Мы здесь, потому что все цифры были равны девяти. Теперь они все установлены в ноль. Затем мы добавляем цифру 1 в начало остальных цифр и возвращаем результат.

😎 Решение:
class Solution {
fun plusOne(digits: IntArray): IntArray {
val n = digits.size
for (i in n - 1 downTo 0) {
if (digits[i] == 9) {
digits[i] = 0
} else {
digits[i]++
return digits
}
}
val result = IntArray(n + 1)
result[0] = 1
return result
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
Задача: 358. Rearrange String k Distance Apart
Сложность: hard

Дана строка s и целое число k, переставьте символы в s так, чтобы одинаковые символы находились на расстоянии не менее k друг от друга. Если невозможно переставить строку, верните пустую строку "".

Пример:
Input: s = "aabbcc", k = 3
Output: "abcabc"
Explanation: The same letters are at least a distance of 3 from each other.


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

1⃣Создайте словарь частот для символов строки и определите максимальную частоту.

2⃣Разделите символы на группы по частоте и создайте сегменты для размещения символов.

3⃣Распределите оставшиеся символы по сегментам, проверяя условия, и объедините сегменты в итоговую строку.

😎 Решение:
class Solution {
fun rearrangeString(s: String, k: Int): String {
val freqs = mutableMapOf<Char, Int>()
var maxFreq = 0

for (char in s) {
freqs[char] = freqs.getOrDefault(char, 0) + 1
maxFreq = maxOf(maxFreq, freqs[char]!!)
}

val mostChars = freqs.filter { it.value == maxFreq }.keys
val secondChars = freqs.filter { it.value == maxFreq - 1 }.keys

val segments = Array(maxFreq) { StringBuilder() }

for (i in 0 until maxFreq) {
for (char in mostChars) {
segments[i].append(char)
}
if (i < maxFreq - 1) {
for (char in secondChars) {
segments[i].append(char)
}
}
}

var segmentId = 0

for ((char, freq) in freqs) {
if (char in mostChars || char in secondChars) {
continue
}

var freq = freq
while (freq > 0) {
segments[segmentId].append(char)
segmentId = (segmentId + 1) % (maxFreq - 1)
freq--
}
}

for (i in 0 until maxFreq - 1) {
if (segments[i].length < k) {
return ""
}
}

return segments.joinToString("") { it.toString() }
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1481. Least Number of Unique Integers after K Removals
Сложность: medium

Дан массив целых чисел arr и целое число k. Найдите минимальное количество уникальных целых чисел после удаления ровно k элементов.

Пример:
Input: arr = [5,5,4], k = 1
Output: 1
Explanation: Remove the single 4, only 5 is left.


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

1⃣Инициализация и построение частотного массива:
Создайте хеш-таблицу для отслеживания частот элементов массива arr.
Итеративно увеличивайте частоту элементов в хеш-таблице.

2⃣Сортировка и удаление элементов:
Создайте массив частот и заполните его значениями из хеш-таблицы.
Отсортируйте массив частот.
Инициализируйте переменную для отслеживания числа удаленных элементов и итеративно добавляйте частоты, пока количество удаленных элементов не превысит k.

3⃣Возвращение результата:
Если количество удаленных элементов превысило k, верните оставшееся количество уникальных элементов.
Если все элементы были удалены, верните 0.

😎 Решение:
class Solution {
fun findLeastNumOfUniqueInts(arr: IntArray, k: Int): Int {
val freqMap = mutableMapOf<Int, Int>()
for (num in arr) {
freqMap[num] = freqMap.getOrDefault(num, 0) + 1
}

val frequencies = freqMap.values.sorted()

var elementsRemoved = 0
for (i in frequencies.indices) {
elementsRemoved += frequencies[i]
if (elementsRemoved > k) {
return frequencies.size - i
}
}

return 0
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 468. Validate IP Address
Сложность: medium

Допустимый IPv4-адрес — это IP в форме "x1.x2.x3.x4", где 0 <= xi <= 255 и xi не может содержать ведущие нули. Например, "192.168.1.1" и "192.168.1.0" являются допустимыми IPv4-адресами, тогда как "192.168.01.1", "192.168.1.00" и "[email protected]" являются недопустимыми IPv4-адресами.

Допустимый IPv6-адрес — это IP в форме "x1:x2:x3:x4:x5:x6:x7
", где:
1 <= xi.length <= 4
xi — это шестнадцатеричная строка, которая может содержать цифры, строчные английские буквы ('a' до 'f') и прописные английские буквы ('A' до 'F').
Ведущие нули в xi допускаются.

Пример:
Input: queryIP = "172.16.254.1"
Output: "IPv4"
Explanation: This is a valid IPv4 address, return "IPv4".


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

1⃣Для проверки адреса IPv4:
Разделить IP на четыре части по разделителю ".".
Проверить каждую подстроку:
Является ли она целым числом между 0 и 255.
Не содержит ли она ведущих нулей (исключение — число "0").

2⃣Для проверки адреса IPv6:
Разделить IP на восемь частей по разделителю ":".
Проверить каждую подстроку:
Является ли она шестнадцатеричным числом длиной от 1 до 4 символов.

3⃣Если IP не соответствует ни одному из форматов, вернуть "Neither".

😎 Решение:
class Solution {
fun validateIPv4(IP: String): String {
val nums = IP.split(".")
for (x in nums) {
if (x.length == 0 || x.length > 3) return "Neither"
if (x[0] == '0' && x.length != 1) return "Neither"
for (ch in x) {
if (!ch.isDigit()) return "Neither"
}
if (x.toInt() > 255) return "Neither"
}
return "IPv4"
}

fun validateIPv6(IP: String): String {
val nums = IP.split(":")
val hexdigits = "0123456789abcdefABCDEF"
for (x in nums) {
if (x.length == 0 || x.length > 4) return "Neither"
for (ch in x) {
if (hexdigits.indexOf(ch) == -1) return "Neither"
}
}
return "IPv6"
}

fun validIPAddress(IP: String): String {
return when {
IP.count { it == '.' } == 3 -> validateIPv4(IP)
IP.count { it == ':' } == 7 -> validateIPv6(IP)
else -> "Neither"
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 957. Prison Cells After N Days
Сложность: medium

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

Вам дан целочисленный массив cells, где cells[i] == 1, если i-я камера занята, и cells[i] == 0, если i-я камера пуста, и вам дано целое число n.
Верните состояние тюрьмы после n дней (т.е. после n таких изменений, описанных выше).

Пример:
Input: cells = [0,1,0,1,1,0,0,1], n = 7
Output: [0,0,1,1,0,0,0,0]
Explanation: The following table summarizes the state of the prison on each day:
Day 0: [0, 1, 0, 1, 1, 0, 0, 1]
Day 1: [0, 1, 1, 0, 0, 0, 0, 0]
Day 2: [0, 0, 0, 0, 1, 1, 1, 0]
Day 3: [0, 1, 1, 0, 0, 1, 0, 0]
Day 4: [0, 0, 0, 0, 0, 1, 0, 0]
Day 5: [0, 1, 1, 1, 0, 1, 0, 0]
Day 6: [0, 0, 1, 0, 1, 1, 0, 0]
Day 7: [0, 0, 1, 1, 0, 0, 0, 0]


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

1⃣Преобразуйте текущее состояние камер в целое число с помощью битовой маски. Это позволит удобно отслеживать повторяющиеся состояния.

2⃣Симулируйте изменение состояния камер день за днем, записывая каждое состояние в хэш-таблицу. Если обнаруживается повторяющееся состояние, вычислите длину цикла и уменьшите количество оставшихся дней с учетом этого цикла.

3⃣Продолжайте симуляцию, пока не достигнете заданного числа дней, либо используйте цикл для ускорения процесса.

😎 Решение:
class Solution {
fun cellsToBitmap(cells: IntArray): Int {
var stateBitmap = 0
for (cell in cells) {
stateBitmap = (stateBitmap shl 1) or cell
}
return stateBitmap
}

fun nextDay(cells: IntArray): IntArray {
val newCells = IntArray(cells.size)
for (i in 1 until cells.size - 1) {
newCells[i] = if (cells[i - 1] == cells[i + 1]) 1 else 0
}
return newCells
}

fun prisonAfterNDays(cells: IntArray, N: Int): IntArray {
var cells = cells
var N = N
val seen = mutableMapOf<Int, Int>()
var isFastForwarded = false

while (N > 0) {
if (!isFastForwarded) {
val stateBitmap = cellsToBitmap(cells)
if (stateBitmap in seen) {
N %= seen[stateBitmap]!! - N
isFastForwarded = true
} else {
seen[stateBitmap] = N
}
}

if (N > 0) {
N--
cells = nextDay(cells)
}
}
return cells
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 298. Binary Tree Longest Consecutive Sequence
Сложность: medium

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

Путь последовательных значений — это путь, где значения увеличиваются на единицу вдоль пути.

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

Пример:
Input: root = [1,null,3,2,4,null,null,null,5]
Output: 3
Explanation: Longest consecutive sequence path is 3-4-5, so return 3.


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

1⃣Инициализация и начало обхода:
Начните обход дерева с корневого узла.
Инициализируйте переменную length, чтобы хранить текущую длину последовательного пути, и передавайте её вниз по дереву.

2⃣Сравнение текущего узла с родительским узлом:
Для каждого узла сравните его значение со значением родительского узла.
Если значение текущего узла на единицу больше значения родительского узла, увеличьте length.
Если значение текущего узла не является последовательным (не больше на единицу), сбросьте length на 1.

3⃣Обход дерева:
Рекурсивно обходите левое и правое поддерево, передавая обновлённое значение length.
В каждом узле обновляйте максимальную длину последовательного пути, если текущая длина больше.

😎 Решение:
class Solution {
private var maxLength = 0

fun longestConsecutive(root: TreeNode?): Int {
dfs(root, null, 0)
return maxLength
}

private fun dfs(node: TreeNode?, parent: TreeNode?, length: Int) {
if (node == null) return
var currentLength = length
if (parent != null && node.`val` == parent.`val` + 1) {
currentLength += 1
} else {
currentLength = 1
}
maxLength = maxOf(maxLength, currentLength)
dfs(node.left, node, currentLength)
dfs(node.right, node, currentLength)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 300. Longest Increasing Subsequence
Сложность: medium

Дан массив целых чисел nums, верните длину самой длинной строго возрастающей подпоследовательности.

Пример:
Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.


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

1⃣Инициализируйте массив dp длиной nums.length, все элементы которого равны 1. dp[i] представляет длину самой длинной возрастающей подпоследовательности, которая заканчивается элементом с индексом i.

2⃣Итерируйтесь от i = 1 до i = nums.length - 1. В каждой итерации используйте второй цикл for для итерации от j = 0 до j = i - 1 (все элементы перед i). Для каждого элемента перед i, проверьте, меньше ли этот элемент, чем nums[i]. Если да, установите dp[i] = max(dp[i], dp[j] + 1).

3⃣Верните максимальное значение из dp.

😎 Решение:
class Solution {
fun lengthOfLIS(nums: IntArray): Int {
if (nums.isEmpty()) return 0

val dp = IntArray(nums.size) { 1 }

for (i in 1 until nums.size) {
for (j in 0 until i) {
if (nums[i] > nums[j]) {
dp[i] = maxOf(dp[i], dp[j] + 1)
}
}
}

return dp.maxOrNull() ?: 0
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 230. Kth Smallest Element in a BST
Сложность: medium

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

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


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

1⃣Инициализация стека и обход в глубину: Инициализируйте стек для хранения узлов дерева. Начните обход дерева в глубину, начиная с корня, и перемещайтесь влево, добавляя каждый узел в стек, пока не достигнете самого левого узла.

2⃣Извлечение узлов и проверка: Когда достигнете самого левого узла, извлеките узел из стека и уменьшите значение k на 1. Если k становится равным нулю, верните значение текущего узла, так как это и есть k-ое по величине значение.

3⃣Переход к правому поддереву: Если k не равен нулю, переместитесь к правому поддереву текущего узла и продолжайте обход, повторяя шаги 1 и 2, пока не найдете k-ое по величине значение.

😎 Решение:
class Solution {
fun kthSmallest(root: TreeNode?, k: Int): Int {
val stack = LinkedList<TreeNode>()
var root = root
var k = k

while (true) {
while (root != null) {
stack.push(root)
root = root.left
}
root = stack.pop()
if (--k == 0) return root.`val`
root = root.right
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 675. Cut Off Trees for Golf Event
Сложность: hard

Вам необходимо срубить все деревья в лесу для проведения гольф-мероприятия. Лес представлен в виде матрицы m x n. В этой матрице:

- 0 означает, что ячейка непроходима.
- 1 представляет собой пустую проходимую ячейку.
- Число больше 1 представляет собой дерево в ячейке, и это число обозначает высоту дерева.

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

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

Начиная с точки (0, 0), верните минимальное количество шагов, необходимых для того, чтобы срубить все деревья. Если невозможно срубить все деревья, верните -1.

Примечание: Входные данные сформированы так, что нет двух деревьев с одинаковой высотой, и нужно срубить как минимум одно дерево.

Пример:
Input: forest = [[1,2,3],[0,0,4],[7,6,5]]
Output: 6
Explanation: Following the path above allows you to cut off the trees from shortest to tallest in 6 steps.


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

1⃣Начиная с (0, 0), для каждого дерева в порядке возрастания высоты будем вычислять расстояние от текущего местоположения до следующего дерева (и перемещаться туда), добавляя это расстояние к ответу.

2⃣Формулируем задачу как предоставление функции расстояния dist(forest, sr, sc, tr, tc), которая вычисляет расстояние от точки (sr, sc) до цели (tr, tc) с учетом препятствий, где dist[i][j] == 0. (Эта функция расстояния вернет -1, если путь невозможен.)

3⃣Далее следует код и анализ сложности, которые общие для всех трех подходов. Затем алгоритмы, представленные в наших подходах, будут сосредоточены только на предоставлении нашей функции dist.

😎 Решение:
class Solution {
fun cutOffTree(forest: List<List<Int>>): Int {
val trees = forest.withIndex().flatMap { (r, row) ->
row.withIndex().mapNotNull { (c, v) ->
if (v > 1) Triple(v, r, c) else null
}
}.sortedBy { it.first }

var sr = 0
var sc = 0
var ans = 0
for ((_, tr, tc) in trees) {
val d = dist(forest, sr, sc, tr, tc)
if (d < 0) return -1
ans += d
sr = tr
sc = tc
}
return ans
}

fun dist(forest: List<List<Int>>, sr: Int, sc: Int, tr: Int, tc: Int): Int {
// Implement the distance function here
return 0
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 253. Meeting Rooms II
Сложность: medium

Дан массив интервалов времени встреч intervals, где intervals[i] = [starti, endi]. Верните минимальное количество необходимых конференц-залов.

Пример:
Input: intervals = [[0,30],[5,10],[15,20]]
Output: 2


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

1⃣Отсортируйте встречи по времени их начала и инициализируйте мин-кучу с временем окончания первой встречи.

2⃣Для каждой последующей встречи проверьте, свободна ли комната (сравните время начала встречи с минимальным временем окончания в куче):
Если свободна, обновите время окончания этой комнаты.
Если не свободна, добавьте новое время окончания в кучу.

3⃣После обработки всех встреч размер кучи будет равен минимальному количеству необходимых комнат.

😎 Решение:
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
Задача: 1530. Number of Good Leaf Nodes Pairs
Сложность: medium

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

Верните количество хороших пар листовых узлов в дереве.

Пример:
Input: root = [1,2,3,null,4], distance = 3
Output: 1
Explanation: The leaf nodes of the tree are 3 and 4 and the length of the shortest path between them is 3. This is the only good pair.


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

1⃣ Инициализируйте список смежности для преобразования дерева в граф и множество для хранения листовых узлов. Используйте вспомогательный метод traverseTree для обхода дерева, чтобы построить граф и найти листовые узлы. В параметрах поддерживайте текущий узел, а также родительский узел. Если текущий узел является листом, добавьте его в множество. В списке смежности добавьте текущий узел в список соседей родительского узла и наоборот. Рекурсивно вызовите traverseTree для левого и правого дочернего узла текущего узла.

2⃣ Инициализируйте переменную ans для подсчета количества хороших пар листовых узлов. Итеративно переберите каждый листовой узел в множестве. Запустите BFS для текущего листового узла. BFS можно прервать досрочно, как только будут обнаружены все узлы, находящиеся на расстоянии от текущего листового узла. Увеличьте ans для каждого листового узла, найденного в каждом запуске BFS.

3⃣ Верните ans / 2. Мы считаем каждую пару дважды, поэтому нужно разделить на 2, чтобы получить фактическое количество.

😎 Решение:
class Solution {
fun countPairs(root: TreeNode?, distance: Int): Int {
val graph = mutableMapOf<TreeNode, MutableList<TreeNode>>()
val leafNodes = mutableSetOf<TreeNode>()
traverseTree(root, null, graph, leafNodes)
var ans = 0
for (leaf in leafNodes) {
val bfsQueue: Queue<TreeNode> = LinkedList()
val seen = mutableSetOf<TreeNode>()
bfsQueue.add(leaf)
seen.add(leaf)
for (i in 0..distance) {
val size = bfsQueue.size
for (j in 0 until size) {
val currNode = bfsQueue.poll()
if (leafNodes.contains(currNode) && currNode != leaf) {
ans++
}
graph[currNode]?.let {
for (neighbor in it) {
if (!seen.contains(neighbor)) {
bfsQueue.add(neighbor)
seen.add(neighbor)
}
}
}
}
}
}
return ans / 2
}

private fun traverseTree(currNode: TreeNode?, prevNode: TreeNode?,
graph: MutableMap<TreeNode, MutableList<TreeNode>>, leafNodes: MutableSet<TreeNode>) {
if (currNode == null) {
return
}
if (currNode.left == null && currNode.right == null) {
leafNodes.add(currNode)
}
if (prevNode != null) {
graph.computeIfAbsent(prevNode) { mutableListOf() }.add(currNode)
graph.computeIfAbsent(currNode) { mutableListOf() }.add(prevNode)
}
traverseTree(currNode.left, currNode, graph, leafNodes)
traverseTree(currNode.right, currNode, graph, leafNodes)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 628. Maximum Product of Three Numbers
Сложность: medium

Задав целочисленный массив nums, найдите три числа, произведение которых максимально, и верните максимальное произведение.

Пример:
Input: nums = [1,2,3]
Output: 6


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

1⃣Отсортируйте массив nums.

2⃣Найдите два возможных максимальных произведения: Произведение трех наибольших элементов массива. Произведение двух наименьших (отрицательных) и одного наибольшего элемента массива.

3⃣Верните максимальное из двух найденных произведений.

😎 Решение:
fun maximumProduct(nums: IntArray): Int {
nums.sort()
val n = nums.size
val max1 = nums[n - 1] * nums[n - 2] * nums[n - 3]
val max2 = nums[0] * nums[1] * nums[n - 1]
return kotlin.math.max(max1, max2)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 259. 3Sum Smaller
Сложность: medium

Дан массив из n целых чисел nums и целое число target. Найдите количество троек индексов i, j, k, удовлетворяющих условию 0 <= i < j < k < n и nums[i] + nums[j] + nums[k] < target.

Пример:
Input: nums = [-2,0,1,3], target = 2
Output: 2
Explanation: Because there are two triplets which sums are less than 2:
[-2,0,1]
[-2,0,3]


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

1⃣Отсортируйте массив nums.

2⃣Для каждого элемента nums[i] от 0 до n-3 найдите количество пар индексов j и k (где i < j < k), таких что nums[i] + nums[j] + nums[k] < target. Используйте функцию twoSumSmaller, которая ищет количество пар с суммой меньше заданного значения.

3⃣В функции twoSumSmaller используйте бинарный поиск для поиска верхней границы индекса k и подсчета количества подходящих пар.

😎 Решение:
class Solution {
fun threeSumSmaller(nums: IntArray, target: Int): Int {
nums.sort()
var sum = 0
for (i in 0 until nums.size - 2) {
sum += twoSumSmaller(nums, i + 1, target - nums[i])
}
return sum
}

private fun twoSumSmaller(nums: IntArray, startIndex: Int, target: Int): Int {
var sum = 0
for (i in startIndex until nums.size - 1) {
val j = binarySearch(nums, i, target - nums[i])
sum += j - i
}
return sum
}

private fun binarySearch(nums: IntArray, startIndex: Int, target: Int): Int {
var left = startIndex
var right = nums.size - 1
while (left < right) {
val mid = (left


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM