Swift | LeetCode
1.49K subscribers
125 photos
1.06K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+bn3i_aLL0-A2ZGMy
Вопросы собесов t.iss.one/+wtkjBoN6OI5hNGEy
Вакансии t.iss.one/+3o9-Ytdiv_E5OGIy
Download Telegram
Задача: 978. Longest Turbulent Subarray
Сложность: medium

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

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

Более формально, подмассив [arr[i], arr[i + 1], ..., arr[j]] массива arr считается турбулентным тогда и только тогда, когда:

Для всех i <= k < j:
arr[k] > arr[k + 1], когда k нечетное, и
arr[k] < arr[k + 1], когда k четное.
Или, для всех i <= k < j:
arr[k] > arr[k + 1], когда k четное, и
arr[k] < arr[k + 1], когда k нечетное.

Пример:
Input: arr = [9,4,2,10,7,8,8,1,9]
Output: 5
Explanation: arr[1] > arr[2] < arr[3] > arr[4] < arr[5]


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

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

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

3⃣Повторяйте шаг 2 до конца массива и верните максимальную длину турбулентного подмассива.

😎 Решение:
class Solution {
func maxTurbulenceSize(_ A: [Int]) -> Int {
let N = A.count
var ans = 1
var anchor = 0

for i in 1..<N {
let c = A[i - 1].compare(to: A[i])
if c == .orderedSame {
anchor = i
} else if i == N - 1 || c.rawValue * A[i].compare(to: A[i + 1]).rawValue != -1 {
ans = max(ans, i - anchor + 1)
anchor = i
}
}

return ans
}
}

extension Int {
func compare(to other: Int) -> ComparisonResult {
if self < other {
return .orderedAscending
} else if self > other {
return .orderedDescending
} else {
return .orderedSame
}
}
}


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

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

Примечание: нельзя использовать встроенные функции, которые оценивают строки как математические выражения, такие как eval().

Пример:
Input: s = "(1+(4+5+2)-3)+(6+8)"
Output: 23


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

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

2⃣Обработка выражения внутри скобок:
Когда встречаем открывающую скобку '(', оцениваем выражение, удаляя операнды и операторы из стека до соответствующей закрывающей скобки.
Результат выражения помещаем обратно в стек.

3⃣Финальная оценка выражения:
Продолжаем процесс до получения финального результата.
Если стек не пуст после обработки всех символов, оцениваем оставшиеся элементы как одно финальное выражение.

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

func evaluateExpr(_ stack: inout [Any]) -> Int {
if stack.isEmpty || !(stack.last is Int) {
stack.append(0)
}

var res = stack.removeLast() as! Int

while !stack.isEmpty && !(stack.last as! Character == ")") {
let sign = stack.removeLast() as! Character

if sign == "+" {
res += stack.removeLast() as! Int
} else {
res -= stack.removeLast() as! Int
}
}
return res
}

func calculate(_ s: String) -> Int {
var operand = 0
var n = 0
var stack: [Any] = []

for ch in s.reversed() {
if ch.isWholeNumber {
operand = Int(pow(10.0, Double(n))) * (ch.wholeNumberValue!) + operand
n += 1
} else if ch != " " {
if n != 0 {
stack.append(operand)
n = 0
operand = 0
}
if ch == "(" {
let res = evaluateExpr(&stack)
stack.removeLast()
stack.append(res)
} else {
stack.append(ch)
}
}
}

if n != 0 {
stack.append(operand)
}

return evaluateExpr(&stack)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1669. Merge In Between Linked Lists
Сложность: medium

Вам даны два связанных списка: list1 и list2 размером n и m соответственно.

Удалите узлы list1 с ath узла по bth узел и вставьте на их место list2.

Синие ребра и узлы на рисунке в вверху поста указывают на результат:

Пример:
Input: list1 = [10,1,13,6,9,5], a = 3, b = 4, list2 = [1000000,1000001,1000002]
Output: [10,1,13,1000000,1000001,1000002,5]
Explanation: We remove the nodes 3 and 4 and put the entire list2 in their place. The blue edges and nodes in the above figure indicate the result.


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

1⃣Инициализация и добавление узлов из list1 до узла a в массив:
Инициализировать переменную index равную 0 и current1 равную list1.
Пока index меньше a, добавлять current1.val в массив mergeArray, перемещаться к следующему узлу current1.next и увеличивать index.

2⃣Добавление узлов из list2 в массив:
Инициализировать current2 равную list2.
Пока current2 не равен null, добавлять current2.val в mergeArray и перемещаться к следующему узлу current2.next.

3⃣Добавление узлов из list1 от узла b + 1 до конца в массив и создание нового связанного списка:
Найти узел на позиции b + 1, перемещая current1 и увеличивая index, пока index меньше b + 1.
Добавлять узлы из current1 в массив, пока current1 не станет null.
Создать новый связанный список из значений в mergeArray, добавляя узлы в начало списка и возвращая его.

😎 Решение:
class Solution {
func mergeInBetween(_ list1: ListNode?, _ a: Int, _ b: Int, _ list2: ListNode?) -> ListNode? {
var mergeArray = [Int]()
var index = 0
var current1 = list1

while index < a {
mergeArray.append(current1!.val)
current1 = current1!.next
index += 1
}

var current2 = list2
while current2 != nil {
mergeArray.append(current2!.val)
current2 = current2!.next
}

while index < b + 1 {
current1 = current1!.next
index += 1
}

while current1 != nil {
mergeArray.append(current1!.val)
current1 = current1!.next
}

var resultList: ListNode? = nil
for val in mergeArray.reversed() {
let newNode = ListNode(val, resultList)
resultList = newNode
}

return resultList
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 588. Design In-Memory File System
Сложность: hard

Спроектируйте структуру данных, которая симулирует файловую систему в памяти.

Реализуйте класс FileSystem:
FileSystem() Инициализирует объект системы.
List<String> ls(String path)
Если path является путем к файлу, возвращает список, содержащий только имя этого файла.
Если path является путем к директории, возвращает список имен файлов и директорий в этой директории.
Ответ должен быть в лексикографическом порядке.
void mkdir(String path) Создает новую директорию согласно заданному пути. Заданная директория не существует. Если промежуточные директории в пути не существуют, вы также должны создать их.
void addContentToFile(String filePath, String content)
Если filePath не существует, создает файл, содержащий заданный контент.
Если filePath уже существует, добавляет заданный контент к исходному содержимому.
String readContentFromFile(String filePath) Возвращает содержимое файла по пути filePath.

Пример:
Input
["FileSystem", "ls", "mkdir", "addContentToFile", "ls", "readContentFromFile"]
[[], ["/"], ["/a/b/c"], ["/a/b/c/d", "hello"], ["/"], ["/a/b/c/d"]]
Output
[null, [], null, null, ["a"], "hello"]

Explanation
FileSystem fileSystem = new FileSystem();
fileSystem.ls("/"); // return []
fileSystem.mkdir("/a/b/c");
fileSystem.addContentToFile("/a/b/c/d", "hello");
fileSystem.ls("/"); // return ["a"]
fileSystem.readContentFromFile("/a/b/c/d"); // return "hello"


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

1⃣ Инициализация файловой системы:
Создайте класс FileSystem, который будет содержать вложенный класс File. Класс File будет представлять либо файл, либо директорию, содержать флаг isfile, словарь files и строку content.

2⃣ Обработка команд:
Реализуйте метод ls, который возвращает список файлов и директорий в указанном пути, либо имя файла, если указанный путь является файлом.
Реализуйте метод mkdir, который создаёт директории по указанному пути. Если промежуточные директории не существуют, создайте их.
Реализуйте метод addContentToFile, который добавляет содержимое в файл по указанному пути. Если файл не существует, создайте его.
Реализуйте метод readContentFromFile, который возвращает содержимое файла по указанному пути.

3⃣ Обработка путей и работа с файлами/директориями:
Используйте метод split для разделения пути на составляющие и навигации по дереву директорий и файлов.
Для каждой команды выполняйте соответствующие операции по созданию, чтению или записи содержимого файлов и директорий.

😎 Решение:
class FileSystem {
class File {
var isFile = false
var files = [String: File]()
var content = ""
}

private var root = File()

private func navigate(_ path: String) -> File {
var t = root
if path != "/" {
let dirs = path.split(separator: "/")
for dir in dirs {
if !dir.isEmpty {
if t.files[String(dir)] == nil {
t.files[String(dir)] = File()
}
t = t.files[String(dir)]!
}
}
}
return t
}

func ls(_ path: String) -> [String] {
let t = navigate(path)
if t.isFile {
return [String(path.split(separator: "/").last!)]
}
return t.files.keys.sorted()
}

func mkdir(_ path: String) {
_ = navigate(path)
}

func addContentToFile(_ filePath: String, _ content: String) {
let t = navigate(filePath)
t.isFile = true
t.content += content
}

func readContentFromFile(_ filePath: String) -> String {
return navigate(filePath).content
}
}


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

Для заданного начала связного списка и целого числа val удалите все узлы связного списка, у которых Node.val равно val, и верните новое начало списка.

Пример:
Input: head = [1,2,6,3,4,5,6], val = 6
Output: [1,2,3,4,5]


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

1⃣Инициализируйте сторожевой узел как ListNode(0) и установите его новым началом: sentinel.next = head. Инициализируйте два указателя для отслеживания текущего узла и его предшественника: curr и prev.

2⃣Пока curr не является нулевым указателем, сравните значение текущего узла со значением для удаления. Если значения равны, удалите текущий узел: prev.next = curr.next, иначе установите предшественника равным текущему узлу. Переместитесь к следующему узлу: curr = curr.next.

3⃣Верните sentinel.next.

😎 Решение:
class ListNode {
var val: Int
var next: ListNode?
init(_ val: Int) {
self.val = val
self.next = nil
}
}

func deleteNode(head: ListNode?, value: Int) -> ListNode? {
let sentinel = ListNode(0)
sentinel.next = head
var prev: ListNode? = sentinel
var curr: ListNode? = head

while curr != nil {
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
Задача: 649. Dota2 Senate
Сложность: medium

В мире Dota2 есть две партии: Radiant и Dire. Сенат Dota2 состоит из сенаторов, представляющих две партии. Теперь сенат хочет принять решение об изменении игры Dota2. Голосование за это изменение проходит в несколько раундов. В каждом раунде каждый сенатор может воспользоваться одним из двух прав: Запретить право одного сенатора: Сенатор может заставить другого сенатора потерять все свои права в этом и всех последующих раундах. Объявить о победе: Если этот сенатор обнаружил, что все сенаторы, у которых еще есть право голоса, принадлежат к одной партии, он может объявить о победе и принять решение об изменении игры. Дана строка senate, представляющая партийную принадлежность каждого сенатора. Символы "R" и "D" обозначают партию Лучезарных и партию Ужасных. Если сенаторов n, то размер данной строки будет равен n. Процедура голосования по кругу начинается от первого сенатора к последнему в заданном порядке. Эта процедура длится до конца голосования. Все сенаторы, потерявшие свои права, будут пропущены во время процедуры. Предположим, что каждый сенатор достаточно умен и будет играть по лучшей стратегии для своей партии. Предскажите, какая партия в итоге объявит о победе и изменит игру в Dota2. На выходе должно получиться "Radiant" или "Dire".

Пример:
Input: senate = "RD"
Output: "Radiant"


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

1⃣Инициализируйте две очереди для хранения индексов сенаторов от партий Radiant и Dire.

2⃣Выполните цикл, пока обе очереди не станут пустыми: Сравните индексы первых сенаторов из обеих очередей. Удалите сенатора с меньшим индексом из очереди и добавьте его с индексом, увеличенным на длину строки. Удалите сенатора с большим индексом из очереди.

3⃣Если одна из очередей опустела, объявите победу партии, чья очередь еще содержит сенаторов.

😎 Решение:
func predictPartyVictory(_ senate: String) -> String {
var radiant = [Int]()
var dire = [Int]()

for (i, s) in senate.enumerated() {
if s == "R" {
radiant.append(i)
} else {
dire.append(i)
}
}

while !radiant.isEmpty && !dire.isEmpty {
let r = radiant.removeFirst()
let d = dire.removeFirst()
if r < d {
radiant.append(r + senate.count)
} else {
dire.append(d + senate.count)
}
}

return radiant.isEmpty ? "Dire" : "Radiant"
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 616. Add Bold Tag in String
Сложность: medium

Вам дана строка s и массив строк words. Вы должны добавить закрытую пару полужирных тегов <b> и </b>, чтобы обернуть подстроки в s, которые существуют в words. Если две такие подстроки пересекаются, вы должны обернуть их вместе только одной парой закрытых полужирных тегов. Если две подстроки, обернутые полужирными тегами, идут подряд, вы должны объединить их. Верните s после добавления полужирных тегов.

Пример:
Input: s = "abcxyz123", words = ["abc","123"]
Output: "<b>abc</b>xyz<b>123</b>"


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

1⃣Найдите все позиции вхождений подстрок из words в строку s и пометьте эти позиции для выделения тегами <b> и </b>.

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

3⃣Постройте новую строку s, добавляя теги <b> и </b> в определенные позиции.

😎 Решение:
func addBoldTag(_ s: String, _ words: [String]) -> String {
var bold = Array(repeating: false, count: s.count)
let sArray = Array(s)

for word in words {
var start = s.startIndex
while let range = s.range(of: word, range: start..<s.endIndex) {
let startIdx = s.distance(from: s.startIndex, to: range.lowerBound)
let endIdx = s.distance(from: s.startIndex, to: range.upperBound)
for i in startIdx..<endIdx {
bold[i] = true
}
start = range.lowerBound < s.index(before: s.endIndex) ? s.index(after: range.lowerBound) : s.endIndex
}
}

var result = ""
var i = 0
while i < sArray.count {
if bold[i] {
result += "<b>"
while i < sArray.count && bold[i] {
result += String(sArray[i])
i += 1
}
result += "</b>"
} else {
result += String(sArray[i])
i += 1
}
}

return result
}


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

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

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

Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.

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


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

1⃣Как видно, вышеописанный алгоритм обратного отслеживания разворачивается как обход дерева в глубину (DFS - Depth-First Search), который часто реализуется с помощью рекурсии.
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.

2⃣Для первого базового случая рекурсивной функции, если remain == 0, то есть мы достигаем желаемой целевой суммы, поэтому мы можем добавить текущую комбинацию в итоговый список.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.

3⃣Помимо вышеупомянутых двух базовых случаев, мы затем продолжаем исследовать подсписок кандидатов, начиная с [start ... n].
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.

😎 Решение:
class Solution {
func backtrack(_ remain: Int, _ comb: inout [Int], _ start: Int, _ candidates: [Int], _ results: inout [[Int]]) {
if remain == 0 {
results.append(comb)
return
} else if remain < 0 {
return
}

for i in start..<candidates.count {
comb.append(candidates[i])
backtrack(remain - candidates[i], &comb, i, candidates, &results)
comb.removeLast()
}
}

func combinationSum(_ candidates: [Int], _ target: Int) -> [[Int]] {
var results = [[Int]]()
var comb = [Int]()

backtrack(target, &comb, 0, candidates, &results)
return results
}
}


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

Числа можно рассматривать как произведение их множителей.

Например, 8 = 2 x 2 x 2 = 2 x 4.
Дано целое число n, верните все возможные комбинации его множителей. Вы можете вернуть ответ в любом порядке.

Обратите внимание, что множители должны быть в диапазоне [2, n - 1].

Пример:
Input: n = 1
Output: []


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

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.

😎 Решение:
class Solution {
private func backtracking(_ factors: inout [Int], _ ans: inout [[Int]]) {
if factors.count > 1 {
ans.append(factors)
}
let lastFactor = factors.removeLast()
for i in (factors.isEmpty ? 2 : factors.last!)...lastFactor {
if i * i > lastFactor { break }
if lastFactor % i == 0 {
factors.append(i)
factors.append(lastFactor / i)
backtracking(&factors, &ans)
factors.removeLast()
factors.removeLast()
}
}
factors.append(lastFactor)
}

func getFactors(_ n: Int) -> [[Int]] {
var ans: [[Int]] = []
var factors: [Int] = [n]
backtracking(&factors, &ans)
return ans
}
}


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

Программа должна была напечатать массив целых чисел. Программа забыла напечатать пробелы, и массив напечатан как строка цифр s, и всё, что мы знаем, это что все числа в массиве были в диапазоне [1, k] и в массиве нет ведущих нулей.

Учитывая строку s и целое число k, верните количество возможных массивов, которые могут быть напечатаны как s с использованием упомянутой программы. Так как ответ может быть очень большим, верните его по модулю 10^9 + 7.

Пример:
Input: s = "1000", k = 10000
Output: 1
Explanation: The only possible array is [1000]


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

1⃣Создать массив dp размера m + 1, чтобы хранить значения dfs(x).

2⃣Для получения значения dfs(start), если dp[start] не равно нулю, вернуть его значение. Иначе:
Если s[start] == 0, вернуть 0.
Если start = m, вернуть 1.
Инициализировать count = 0, чтобы считать количество возможных массивов.
Перебрать все возможные конечные индексы end, и если s[start ~ end] представляет допустимое число, продолжить рекурсивный вызов dfs(end + 1) и обновить count как count += dfs(end + 1).
Обновить dp[start] значением dfs(start).

3⃣Вернуть dfs(0).

😎 Решение:
class Solution {
let mod = 1_000_000_007

func dfs(_ dp: inout [Int], _ start: Int, _ s: String, _ k: Int) -> Int {
if dp[start] != 0 {
return dp[start]
}
if start == s.count {
return 1
}
if s[s.index(s.startIndex, offsetBy: start)] == "0" {
return 0
}

var count = 0
for end in start..<s.count {
let startIndex = s.index(s.startIndex, offsetBy: start)
let endIndex = s.index(s.startIndex, offsetBy: end + 1)
let currNumber = String(s[startIndex..<endIndex])
if Int(currNumber)! > k {
break
}
count = (count + dfs(&dp, end + 1, s, k)) % mod
}

dp[start] = count
return count
}

func numberOfArrays(_ s: String, _ k: Int) -> Int {
var dp = Array(repeating: 0, count: s.count + 1)
return dfs(&dp, 0, s, k)
}
}


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

Вам дана целочисленная матричная сетка m x n и три целых числа row, col и color. Каждое значение в сетке представляет собой цвет квадрата сетки в данном месте. Два квадрата называются смежными, если они находятся рядом друг с другом в любом из 4 направлений. Два квадрата принадлежат одному связанному компоненту, если они имеют одинаковый цвет и являются смежными.

Граница связанного компонента - это все квадраты в связанном компоненте, которые либо смежны (по крайней мере) с квадратом, не входящим в компонент, либо находятся на границе сетки (в первой или последней строке или столбце). Вы должны окрасить границу связанного компонента, содержащего квадрат grid[row][col], в цвет. Верните конечную сетку.

Пример:
Input: grid = [[1,1],[1,2]], row = 0, col = 0, color = 3
Output: [[3,3],[3,2]]


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

1⃣Поиск связанного компонента:
Используйте поиск в глубину (DFS) или поиск в ширину (BFS), чтобы найти все клетки, принадлежащие связанному компоненту, содержащему клетку grid[row][col].
Запомните все клетки, которые принадлежат этому компоненту.

2⃣Определение границ компонента:
Для каждой клетки в связанном компоненте проверьте, является ли она границей. Клетка является границей, если она находится на краю сетки или если хотя бы одна из её соседних клеток не принадлежит связанному компоненту или имеет другой цвет.

3⃣Окрашивание границы:
Измените цвет всех клеток, являющихся границами, на заданный цвет.

😎 Решение:
class Solution {
func colorBorder(_ grid: [[Int]], _ row: Int, _ col: Int, _ color: Int) -> [[Int]] {
var grid = grid
let m = grid.count, n = grid[0].count
let original_color = grid[row][col]
var visited = Set<[Int]>()
var borders = [[Int]]()

func dfs(_ r: Int, _ c: Int) {
visited.insert([r, c])
var is_border = false
for (dr, dc) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
let nr = r + dr, nc = c + dc
if nr >= 0 && nr < m && nc >= 0 && nc < n {
if !visited.contains([nr, nc]) {
if grid[nr][nc] == original_color {
dfs(nr, nc)
} else {
is_border = true
}
}
} else {
is_border = true
}
}
if is_border || r == 0 || r == m - 1 || c == 0 || c == n - 1 {
borders.append([r, c])
}
}

dfs(row, col)
for border in borders {
grid[border[0]][border[1]] = color
}

return grid
}
}


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

Дан массив точек в плоскости X-Y points, где points[i] = [xi, yi]. Верните минимальную площадь прямоугольника, образованного из этих точек, со сторонами, параллельными осям X и Y. Если такого прямоугольника не существует, верните 0.

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


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

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

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

3⃣Если минимальная площадь остается бесконечной, вернуть 0. Иначе вернуть минимальную площадь.

😎 Решение:
class Solution {
func minAreaRect(_ points: [[Int]]) -> Int {
var pointSet = Set(points.map { "\($0[0]),\($0[1])" })
var minArea = Int.max

for i in 0..<points.count {
for j in i+1..<points.count {
let x1 = points[i][0], y1 = points[i][1]
let x2 = points[j][0], y2 = points[j][1]
if x1 != x2 && y1 != y2 && pointSet.contains("\(x1),\(y2)") && pointSet.contains("\(x2),\(y1)") {
minArea = min(minArea, abs(x2 - x1) * abs(y2 - y1))
}
}
}

return minArea == Int.max ? 0 : minArea
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 363. Max Sum of Rectangle No Larger Than K
Сложность: hard

Дана матрица размером m x n и целое число k, вернуть максимальную сумму прямоугольника в матрице, такая что его сумма не превышает k.

Гарантируется, что будет прямоугольник с суммой, не превышающей k.

Пример:
Input: matrix = [[1,0,1],[0,-2,3]], k = 2
Output: 2
Explanation: Because the sum of the blue rectangle [[0, 1], [-2, 3]] is 2, and 2 is the max number no larger than k (k = 2).


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

1⃣Создать вспомогательную функцию updateResult, которая будет находить максимальную сумму подмассива в одномерном массиве, не превышающую k.

2⃣Преобразовать каждую подматрицу в одномерный массив и применить к ней функцию updateResult.

3⃣Вернуть максимальную найденную сумму.

😎 Решение:
class Solution {
var result = Int.min

func updateResult(_ nums: [Int], _ k: Int) {
var sum = 0
var sortedSum = Set<Int>()
sortedSum.insert(0)

for num in nums {
sum += num
if let x = sortedSum.first(where: { $0 >= sum - k }) {
result = max(result, sum - x)
}
sortedSum.insert(sum)
}
}

func maxSumSubmatrix(_ matrix: [[Int]], _ k: Int) -> Int {
let rows = matrix.count
let cols = matrix[0].count
var rowSum = [Int](repeating: 0, count: cols)

for i in 0..<rows {
rowSum = [Int](repeating: 0, count: cols)
for row in i..<rows {
for col in 0..<cols {
rowSum[col] += matrix[row][col]
}
updateResult(rowSum, k)
if result == k {
return result
}
}
}
return result
}
}


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

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

Длина пути между двумя узлами представлена количеством рёбер между ними.

Пример:
Input: root = [5,4,5,1,1,null,5]
Output: 2
Explanation: The shown image shows that the longest path of the same value (i.e. 5).


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

1⃣Определить рекурсивную функцию solve(), принимающую текущий узел root и значение родительского узла parent. Если root равен NULL, вернуть 0. Рекурсивно вызвать solve() для левого и правого дочернего узлов, передав значение текущего узла как значение родительского узла.

2⃣Обновить переменную ans, если сумма значений для левого и правого узлов больше текущего значения ans. Если значение текущего узла равно значению родительского узла, вернуть max(left, right) + 1, иначе вернуть 0.

3⃣Вызвать solve() с корневым узлом и значением родительского узла -1. Вернуть максимальную длину пути ans..

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

func solve(_ root: TreeNode?, _ parent: Int) -> Int {
guard let root = root else { return 0 }

let left = solve(root.left, root.val)
let right = solve(root.right, root.val)

ans = max(ans, left + right)

return root.val == parent ? max(left, right) + 1 : 0
}

func longestUnivaluePath(_ root: TreeNode?) -> Int {
solve(root, -1)
return ans
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1282. Group the People Given the Group Size They Belong To
Сложность: medium

Есть n человек, которые разделены на неизвестное количество групп. Каждый человек имеет уникальный ID от 0 до n - 1.

Вам дан целочисленный массив groupSizes, где groupSizes[i] - это размер группы, в которой находится человек i. Например, если groupSizes[1] = 3, то человек 1 должен быть в группе размером 3.

Верните список групп таким образом, чтобы каждый человек i находился в группе размером groupSizes[i].

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

Пример:
Input: groupSizes = [3,3,3,3,3,1,3]
Output: [[5],[0,1,2],[3,4,6]]
Explanation:
The first group is [5]. The size is 1, and groupSizes[5] = 1.
The second group is [0,1,2]. The size is 3, and groupSizes[0] = groupSizes[1] = groupSizes[2] = 3.
The third group is [3,4,6]. The size is 3, and groupSizes[3] = groupSizes[4] = groupSizes[6] = 3.
Other possible solutions are [[2,1,6],[5],[0,4,3]] and [[5],[0,6,2],[4,3,1]].


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

1⃣Инициализируйте пустой список списков ans для хранения индексов групп. Создайте хэш-таблицу szToGroup, где ключи — целые числа, представляющие размеры групп, а значения — массивы соответствующих индексов в группе.

2⃣Итерируйте по массиву groupSizes, для каждого индекса i:
Вставьте индекс i в список szToGroup[groupSizes[i]].
Если размер списка становится равным groupSizes[i], добавьте его в ans и очистите массив для ключа groupSizes[i] в хэш-таблице szToGroup.

3⃣Верните ans.


😎 Решение:
class Solution {
func groupThePeople(_ groupSizes: [Int]) -> [[Int]] {
var ans = [[Int]]()
var szToGroup = [Int: [Int]]()

for i in 0..<groupSizes.count {
let size = groupSizes[i]
if szToGroup[size] == nil {
szToGroup[size] = [Int]()
}
szToGroup[size]!.append(i)

if szToGroup[size]!.count == size {
ans.append(szToGroup[size]!)
szToGroup[size] = [Int]()
}
}

return ans
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1437. Check If All 1's Are at Least Length K Places Away
Сложность: easy

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

Пример:
Input: nums = [1,0,0,0,1,0,0,1], k = 2
Output: true
Explanation: Each of the 1s are at least 2 places away from each other.


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

1⃣Инициализировать счетчик нулей значением k для учета первого появления единицы.

2⃣Итерировать по массиву nums, проверяя, если текущий элемент равен 1. Если число нулей между единицами меньше k, вернуть false; иначе сбросить счетчик нулей на 0.

3⃣Если текущий элемент равен 0, увеличить счетчик нулей. В конце итерации вернуть true.

😎 Решение:
class Solution {
func kLengthApart(_ nums: [Int], _ k: Int) -> Bool {
var count = k
for num in nums {
if num == 1 {
if count < k {
return false
}
count = 0
} else {
count += 1
}
}
return true
}
}


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

Мы определяем правильное использование заглавных букв в слове, когда выполняется одно из следующих условий:

Все буквы в этом слове заглавные, как "USA".
Все буквы в этом слове строчные, как "leetcode".
Только первая буква в этом слове заглавная, как "Google".
Дана строка word. Верните true, если использование заглавных букв в ней правильное.

Пример:
Input: word = "USA"
Output: true


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

1⃣Шаблон для первого случая в регулярном выражении: [A-Z]*, где [A-Z] соответствует одной заглавной букве от 'A' до 'Z', а * означает повторение предыдущего шаблона 0 или более раз. Этот шаблон представляет "Все заглавные буквы".

2⃣Шаблон для второго случая в регулярном выражении: [a-z]*, где [a-z] соответствует одной строчной букве от 'a' до 'z'. Этот шаблон представляет "Все строчные буквы". Шаблон для третьего случая в регулярном выражении: [A-Z][a-z]*, где первая буква заглавная, а остальные строчные.

3⃣Объедините эти три шаблона: [A-Z]*|[a-z]*|[A-Z][a-z]*, где | означает "или". Мы можем объединить второй и третий случай, получив . [a-z]*, где . соответствует любому символу. Итоговый шаблон: [A-Z]*|.[a-z]*.

😎 Решение:
class Solution {
func detectCapitalUse(_ word: String) -> Bool {
let pattern = "^[A-Z]*$|^[a-z]*$|^[A-Z][a-z]*$"
let regex = try! NSRegularExpression(pattern: pattern)
let range = NSRange(location: 0, length: word.utf16.count)
return regex.firstMatch(in: word, options: [], range: range) != nil
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1675. Minimize Deviation in Array
Сложность: hard

Вам дан массив nums из n положительных целых чисел.

Вы можете выполнять два типа операций над любым элементом массива любое количество раз:
Если элемент четный, разделите его на 2.
Например, если массив [1,2,3,4], то вы можете выполнить эту операцию на последнем элементе, и массив станет [1,2,3,2].
Если элемент нечетный, умножьте его на 2.
Например, если массив [1,2,3,4], то вы можете выполнить эту операцию на первом элементе, и массив станет [2,2,3,4].
Отклонение массива — это максимальная разница между любыми двумя элементами в массиве.

Верните минимальное отклонение, которое может иметь массив после выполнения некоторого числа операций.

Пример:
Input: nums = [1,2,3,4]
Output: 1
Explanation: You can transform the array to [1,2,3,2], then to [2,2,3,2], then the deviation will be 3 - 2 = 1.


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

1⃣Инициализируйте max-heap под названием evens. Если элемент массива четный, добавьте его в evens; если элемент нечетный, умножьте его на 2 и добавьте в evens. Одновременно отслеживайте минимальный элемент в evens.

2⃣Извлекайте максимальный элемент из evens, обновляйте минимальное отклонение, используя максимальный элемент и текущий минимальный элемент. Если максимальный элемент четный, делите его на 2 и снова добавляйте в evens.

3⃣Повторяйте шаг 2 до тех пор, пока максимальный элемент в evens не станет нечетным. Верните минимальное отклонение.

😎 Решение:
class Solution {
func minimumDeviation(_ nums: [Int]) -> Int {
var evens = PriorityQueue<Int>(ascending: false)
var minimum = Int.max

for num in nums {
if num % 2 == 0 {
evens.push(num)
minimum = min(minimum, num)
} else {
evens.push(num * 2)
minimum = min(minimum, num * 2)
}
}

var minDeviation = Int.max

while let currentValue = evens.pop() {
minDeviation = min(minDeviation, currentValue - minimum)
if currentValue % 2 == 0 {
evens.push(currentValue / 2)
minimum = min(minimum, currentValue / 2)
} else {
break
}
}

return minDeviation
}
}


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

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

Реализуйте класс Solution:
Solution() Инициализирует объект системы.
String encode(String longUrl) Возвращает короткий URL для данного longUrl.
String decode(String shortUrl) Возвращает исходный длинный URL для данного shortUrl. Гарантируется, что данный shortUrl был закодирован тем же объектом.

Пример:
Input: url = "https://leetcode.com/problems/design-tinyurl"
Output: "https://leetcode.com/problems/design-tinyurl"

Explanation:
Solution obj = new Solution();
string tiny = obj.encode(url); // returns the encoded tiny url.
string ans = obj.decode(tiny); // returns the original url after decoding it.


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

1⃣ Инициализация:
Создайте строку, содержащую все возможные символы (цифры и буквы), которые могут быть использованы для генерации кода.
Создайте хэш-таблицу для хранения соответствий коротких и длинных URL-адресов.
Создайте объект для генерации случайных чисел.

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

3⃣ Декодирование:
Удалите префикс короткого URL, чтобы получить код.
Используйте код для поиска длинного URL в хэш-таблице.
Верните длинный URL.

😎 Решение:
class Codec {
private let alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
private var map = [String: String]()
private var rand = SystemRandomNumberGenerator()

private func getRand() -> String {
var result = ""
for _ in 0..<6 {
result.append(alphabet.randomElement()!)
}
return result
}

func encode(_ longUrl: String) -> String {
var key = getRand()
while map[key] != nil {
key = getRand()
}
map[key] = longUrl
return "https://tinyurl.com/" + key
}

func decode(_ shortUrl: String) -> String {
let key = String(shortUrl.dropFirst("https://tinyurl.com/".count))
return map[key] ?? ""
}
}


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

Вам дан массив целых чисел nums. Существует скользящее окно размера k, которое перемещается с самого левого конца массива до самого правого. Вы можете видеть только k чисел в окне. Каждый раз скользящее окно перемещается вправо на одну позицию.

Верните максимальные значения скользящего окна.

Пример:
Input: nums = [1], k = 1
Output: [1]


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

1⃣Инициализация и заполнение первой части окна:
Создайте двустороннюю очередь dq для хранения индексов элементов и список res для хранения результатов.
Пройдите по первым k элементам массива nums (от i = 0 до k - 1). Для каждого элемента:
Удалите из dq все элементы, которые меньше или равны текущему элементу nums[i].
Добавьте текущий индекс i в конец dq.
Добавьте в res максимальный элемент первого окна, который находится в nums[dq[0]].

2⃣Сканирование оставшейся части массива:
Пройдите по оставшимся элементам массива nums (от i = k до n - 1). Для каждого элемента:
Если индекс элемента на передней части dq равен i - k, удалите этот элемент из dq, так как он выходит за пределы текущего окна.
Удалите из dq все элементы, которые меньше или равны текущему элементу nums[i].
Добавьте текущий индекс i в конец dq.
Добавьте в res максимальный элемент текущего окна, который находится в nums[dq[0]].

3⃣Возвращение результата:
Верните список res, содержащий максимальные элементы для каждого скользящего окна.

😎 Решение:
class Solution {
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
var dq = [Int]()
var res = [Int]()

for i in 0..<k {
while !dq.isEmpty && nums[i] >= nums[dq.last!] {
dq.removeLast()
}
dq.append(i)
}
res.append(nums[dq.first!])

for i in k..<nums.count {
if dq.first == i - k {
dq.removeFirst()
}
while !dq.isEmpty && nums[i] >= nums[dq.last!] {
dq.removeLast()
}
dq.append(i)
res.append(nums[dq.first!])
}

return res
}
}


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