Golang | LeetCode
3.93K subscribers
177 photos
1.07K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.me/+MVqzqT6ZzFFhYjhi
Вопросы собесов t.me/+ajHN0OKU1okyZDky
Вакансии t.me/+mX_RBWjiMTExODUy
Download Telegram
Задача: 96. Unique Binary Search Trees
Сложность: medium

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

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


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

1⃣Определим G(n):
- G(n) — количество уникальных BST с n узлами.
- Базовые случаи: G(0) = 1, G(1) = 1.

2⃣Рекурсия через выбор корня:
- Пусть i — корень дерева. Тогда:
- Левое поддерево: G(i-1) узлов.
- Правое поддерево: G(n-i) узлов.
- Всего деревьев при корне i: G(i-1) * G(n-i).

3⃣Результат:
- G(n) = ∑ G(i-1) * G(n-i) для всех i от 1 до n.
- Используем динамическое программирование для подсчета.

😎 Решение:
func numTrees(n int) int {
G := make([]int, n+1)
G[0] = 1
G[1] = 1

for i := 2; i <= n; i++ {
for j := 1; j <= i; j++ {
G[i] += G[j-1] * G[i-j]
}
}

return G[n]
}


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

У вас есть строка jewels, содержащая типы драгоценных камней, и строка stones, содержащая ваши камни.
Нужно вернуть количество камней, которые являются драгоценностями.
Регистр важен: "a" и "A" — разные типы.

Пример:
Input: jewels = "aA", stones = "aAAbbbb"
Output: 3


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

1⃣Преобразуем строку jewels во множество (map[rune]bool), чтобы быстро проверять, является ли символ драгоценностью.

2⃣Проходим по строке stones и для каждого символа проверяем его наличие в множестве.

3⃣Если символ найден в множестве, увеличиваем счетчик.

😎 Решение:
func numJewelsInStones(jewels string, stones string) int {
jewelSet := make(map[rune]bool)
for _, ch := range jewels {
jewelSet[ch] = true
}

count := 0
for _, ch := range stones {
if jewelSet[ch] {
count++
}
}

return count
}


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

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

Пример:
Input: s = "bcabc"
Output: "abc"


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

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

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

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

😎 Решение:
func removeDuplicateLetters(s string) string {
stack := []rune{}
seen := make(map[rune]bool)
lastOccurrence := make(map[rune]int)
for i, c := range s {
lastOccurrence[c] = i
}

for i, c := range s {
if !seen[c] {
for len(stack) > 0 && c < stack[len(stack)-1] && i < lastOccurrence[stack[len(stack)-1]] {
seen[stack[len(stack)-1]] = false
stack = stack[:len(stack)-1]
}
seen[c] = true
stack = append(stack, c)
}
}
return string(stack)
}


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

Допустимым кодированием массива слов является любая опорная строка s и массив индексов indices, такие что:

words.length == indices.length
Опорная строка s заканчивается символом '#'.
Для каждого индекса indices[i], подстрока строки s, начинающаяся с indices[i] и заканчивающаяся (но не включительно) следующим символом '#', равна words[i].
Дан массив слов, верните длину самой короткой возможной опорной строки s для любого допустимого кодирования слов.

Пример:
Input: words = ["time", "me", "bell"]
Output: 10
Explanation: A valid encoding would be s = "time#bell#" and indices = [0, 2, 5].
words[0] = "time", the substring of s starting from indices[0] = 0 to the next '#' is underlined in "time#bell#"
words[1] = "me", the substring of s starting from indices[1] = 2 to the next '#' is underlined in "time#bell#"
words[2] = "bell", the substring of s starting from indices[2] = 5 to the next '#' is underlined in "time#bell#"


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

1⃣Поскольку слово имеет не более 6 собственных суффиксов (так как words[i].length <= 7), давайте итерироваться по всем из них. Для каждого собственного суффикса мы попытаемся удалить его из нашего списка слов. Для эффективности сделаем words множеством.

2⃣Затем создадим список оставшихся слов и сформируем опорную строку, объединяя каждое слово с символом '#'.

3⃣В конце вернем длину полученной опорной строки.

😎 Решение:
func minimumLengthEncoding(words []string) int {
good := make(map[string]struct{})
for _, word := range words {
good[word] = struct{}{}
}
for _, word := range words {
for k := 1; k < len(word); k++ {
delete(good, word[k:])
}
}
length := 0
for word := range good {
length += len(word) + 1
}
return length
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Задача: 208. Implement Trie (Prefix Tree)
Сложность: medium

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


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

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.

😎 Решение:
package main

type TrieNode struct {
links [26]*TrieNode
isEnd bool
}

func (node *TrieNode) containsKey(ch byte) bool {
return node.links[ch-'a'] != nil
}

func (node *TrieNode) get(ch byte) *TrieNode {
return node.links[ch-'a']
}

func (node *TrieNode) put(ch byte, newNode *TrieNode) {
node.links[ch-'a'] = newNode
}

func (node *TrieNode) setEnd() {
node.isEnd = true
}

func (node *TrieNode) isEndNode() bool {
return node.isEnd
}

type Trie struct {
root *TrieNode
}

func Constructor() Trie {
return Trie{root: &TrieNode{}}
}

func (this *Trie) Insert(word string) {
node := this.root
for i := 0; i < len(word); i++ {
ch := word[i]
if !node.containsKey(ch) {
node.put(ch, &TrieNode{})
}
node = node.get(ch)
}
node.setEnd()
}

func (this *Trie) searchPrefix(word string) *TrieNode {
node := this.root
for i := 0; i < len(word); i++ {
ch := word[i]
if node.containsKey(ch) {
node = node.get(ch)
} else {
return nil
}
}
return node
}

func (this *Trie) Search(word string) bool {
node := this.searchPrefix(word)
return node != nil && node.isEndNode()
}

func (this *Trie) StartsWith(prefix string) bool {
return this.searchPrefix(prefix) != nil
}


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

Дано целое число n, верните true, если и только если оно является числом Армстронга.

k-значное число n является числом Армстронга, если сумма k-й степени каждой его цифры равна n.

Пример:
Input: n = 153
Output: true
Explanation: 153 is a 3-digit number, and 153 = 1^3 + 5^3 + 3^3.


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

1⃣Получите количество цифр в n, преобразовав его в строку и найдя длину.

2⃣Создайте функцию getSumOfKthPowerOfDigits(n, k), которая возвращает сумму k-й степени каждой цифры числа n.
Инициализируйте переменную result для хранения результата.
Пока n не равно 0, добавляйте k-ю степень последней цифры n к result и удаляйте последнюю цифру.

3⃣Верните true, если результат равен исходному числу n.

😎 Решение:
import "math"

func getSumOfKthPowerOfDigits(n, k int) int {
result := 0
for n != 0 {
digit := n % 10
result += int(math.Pow(float64(digit), float64(k)))
n /= 10
}
return result
}

func isArmstrong(n int) bool {
length := len(strconv.Itoa(n))
return getSumOfKthPowerOfDigits(n, length) == n
}


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

Дана голова связного списка. Верните узел, с которого начинается цикл. Если цикла нет, верните null.

Цикл в связном списке существует, если есть такой узел в списке, до которого можно добраться снова, последовательно следуя по указателю next. Внутренне переменная pos используется для обозначения индекса узла, к которому подключен указатель next последнего узла (индексация с нуля). Она равна -1, если цикла нет. Обратите внимание, что pos не передается в качестве параметра.

Не модифицируйте связный список.

Пример:
Input: head = [3,2,0,-4], pos = 1
Output: tail connects to node index 1
Explanation: There is a cycle in the linked list, where tail connects to the second node.


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

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

2⃣Проверка на наличие узла в множестве:
Для каждого посещенного узла проверьте, содержится ли он уже в множестве nodes_seen.
Если узел найден в множестве, это означает, что был найден цикл. Верните текущий узел как точку входа в цикл.

3⃣Добавление узла в множество или завершение обхода:
Если узел не найден в nodes_seen, добавьте его в множество и перейдите к следующему узлу.
Если узел становится равным null (конец списка), верните null. В списке нет цикла, так как в случае наличия цикла вы бы застряли в петле и не достигли бы конца списка.

😎 Решение:
type ListNode struct {
Val int
Next *ListNode
}

func detectCycle(head *ListNode) *ListNode {
nodesSeen := make(map[*ListNode]bool)
node := head
for node != nil {
if _, ok := nodesSeen[node]; ok {
return node
} else {
nodesSeen[node] = true
node = node.Next
}
}
return nil
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 1325. Delete Leaves With a Given Value
Сложность: medium

Дано корневое дерево root и целое число target. Удалите все листовые узлы со значением target.

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

Пример:
Input: root = [1,2,3,2,null,2,4], target = 2
Output: [1,null,3,null,4]
Explanation: Leaf nodes in green with value (target = 2) are removed (Picture in left).
After removing, new nodes become leaf nodes with value (target = 2) (Picture in center).


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

1⃣Базовый случай: Если root равен null, верните null, чтобы обработать условия пустого дерева или прохождения за пределы листовых узлов.

2⃣Рекурсивный обход: Выполните обход в постфиксном порядке, чтобы гарантировать обработку всех потомков перед текущим узлом (root):
— Рекурсивно вызовите removeLeafNodes для левого дочернего узла root и обновите левый дочерний узел возвращаемым значением.
— Аналогично, рекурсивно вызовите removeLeafNodes для правого дочернего узла root и обновите правый дочерний узел возвращаемым значением.

3⃣Оценка узла:
— Проверьте, является ли текущий узел root листовым узлом и совпадает ли его значение с target. Если оба условия выполнены, верните null, чтобы эффективно удалить узел, не присоединяя его к родителю.
— Если узел не является листом или не совпадает с target, верните сам root.

😎 Решение
func removeLeafNodes(root *TreeNode, target int) *TreeNode {
if root == nil {
return nil
}

root.Left = removeLeafNodes(root.Left, target)
root.Right = removeLeafNodes(root.Right, target)

if root.Left == nil && root.Right == nil && root.Val == target {
return nil
}
return root
}


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

Дано beginWord, endWord и wordList. Нужно найти все кратчайшие последовательности преобразования beginWord -> s1 -> s2 -> ... -> sk, где:

- Каждое слово отличается от предыдущего ровно одной буквой.
- Каждое слово есть в wordList.
- Последнее слово sk совпадает с endWord.

Пример:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]  
Output: [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]


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

1⃣Добавляем wordList в wordSet для быстрого поиска.

2⃣Запускаем BFS, строим adjList — список смежности, где храним связи между словами.

3⃣Используем backtracking, чтобы восстановить пути от endWord к beginWord.

😎 Решение:
func findLadders(beginWord, endWord string, wordList []string) [][]string {
adjList := make(map[string][]string)
wordSet := make(map[string]bool)
for _, word := range wordList {
wordSet[word] = true
}

bfs(beginWord, wordSet, adjList)
var currPath []string
var shortestPaths [][]string
currPath = append(currPath, endWord)
backtrack(endWord, beginWord, &currPath, &shortestPaths, adjList)
return shortestPaths
}

func findNeighbors(word string, wordSet map[string]bool) []string {
var neighbors []string
charList := []rune(word)
for i := range charList {
oldChar := charList[i]
for c := 'a'; c <= 'z'; c++ {
if c != oldChar {
charList[i] = c
newWord := string(charList)
if wordSet[newWord] {
neighbors = append(neighbors, newWord)
}
}
}
charList[i] = oldChar
}
return neighbors
}

func backtrack(source, destination string, currPath *[]string, shortestPaths *[][]string, adjList map[string][]string) {
if source == destination {
pathCopy := make([]string, len(*currPath))
copy(pathCopy, *currPath)
reverse(pathCopy)
*shortestPaths = append(*shortestPaths, pathCopy)
return
}
for _, neighbor := range adjList[source] {
*currPath = append(*currPath, neighbor)
backtrack(neighbor, destination, currPath, shortestPaths, adjList)
*currPath = (*currPath)[:len(*currPath)-1]
}
}

func bfs(beginWord string, wordSet map[string]bool, adjList map[string][]string) {
queue := []string{beginWord}
visited := make(map[string]bool)
visited[beginWord] = true

for len(queue) > 0 {
current := queue[0]
queue = queue[1:]
neighbors := findNeighbors(current, wordSet)
for _, neighbor := range neighbors {
if !visited[neighbor] {
visited[neighbor] = true
queue = append(queue, neighbor)
adjList[neighbor] = append(adjList[neighbor], current)
}
}
}
}

func reverse(slice []string) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
}


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

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

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

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

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


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

1⃣Давайте воспользуемся порядком узлов при симметричном обходе (inorder traversal):
Левый -> Узел -> Правый.
Постордер:
Здесь узлы перечисляются в порядке их посещения, и вы можете следовать последовательности 1-2-3-4-5 для сравнения различных стратегий.
Порядок "Левый -> Узел -> Правый" при симметричном обходе означает, что для BST каждый элемент должен быть меньше следующего.

2⃣Следовательно, алгоритм с временной сложностью O(N) и пространственной сложностью O(N) может быть простым:
Вычислить список симметричного обхода inorder.
Проверить, меньше ли каждый элемент в списке inorder следующего за ним.

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

😎 Решение:
var prev *TreeNode

func inorder(root *TreeNode) bool {
if root == nil {
return true
}
if !inorder(root.Left) {
return false
}
if prev != nil && root.Val <= prev.Val {
return false
}
prev = root
return inorder(root.Right)
}

func isValidBST(root *TreeNode) bool {
prev = nil
return inorder(root)
}


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

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

Пример:
Input: s = "cba", k = 1
Output: "acb"


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

1⃣Если k равно 1, найти лексикографически наименьшую строку путем вращения строки и поиска минимального варианта.

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

3⃣Вернуть результат.

😎 Решение:
package main

import (
"sort"
"strings"
)

func orderlyQueue(s string, k int) string {
if k == 1 {
minString := s
for i := 1; i < len(s); i++ {
rotated := s[i:] + s[:i]
if rotated < minString {
minString = rotated
}
}
return minString
} else {
sArr := strings.Split(s, "")
sort.Strings(sArr)
return strings.Join(sArr, "")
}
}


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

Дан массив nums размера n, верните элемент большинства.

Элемент большинства — это элемент, который встречается более чем ⌊n / 2⌋ раз. Можно предположить, что элемент большинства всегда существует в массиве.

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


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

1⃣Использование HashMap для подсчета:
Создайте HashMap для отслеживания количества каждого элемента в массиве.

2⃣Подсчет вхождений элементов:
Пройдите по массиву nums, увеличивая счетчик в HashMap для каждого элемента.

3⃣Поиск элемента большинства:
Определите элемент большинства, просмотрев HashMap и найдя ключ с максимальным значением, которое должно быть больше ⌊n / 2⌋.

😎 Решение:
func majorityElement(nums []int) int {
counts := make(map[int]int)
for _, num := range nums {
if _, ok := counts[num]; ok {
counts[num]++
} else {
counts[num] = 1
}
}
for num, count := range counts {
if count > len(nums)/2 {
return num
}
}
return 0
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2💊1
Задача: 1312. Minimum Insertion Steps to Make a String Palindrome
Сложность: hard

Дана строка s. За один шаг вы можете вставить любой символ в любой индекс строки.

Верните минимальное количество шагов, необходимых для превращения s в палиндром.

Палиндром — это строка, которая читается одинаково как вперед, так и назад.

Пример:
Input: s = "zzazz"
Output: 0
Explanation: The string "zzazz" is already palindrome we do not need any insertions.


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

1⃣Создайте целочисленную переменную n и инициализируйте её размером строки s. Создайте строковую переменную sReverse и установите её значение как обратную строку s.

2⃣Создайте двумерный массив memo размером n + 1 на n + 1, где memo[i][j] будет содержать длину наибольшей общей подпоследовательности, учитывая первые i символов строки s и первые j символов строки sReverse. Инициализируйте массив значением -1.

3⃣Верните n - lcs(s, sReverse, n, n, memo), где lcs - это рекурсивный метод с четырьмя параметрами: первая строка s1, вторая строка s2, длина подстроки от начала s1, длина подстроки от начала s2 и memo. Метод возвращает длину наибольшей общей подпоследовательности в подстроках s1 и s2. В этом методе выполните следующее:

Если m == 0 или n == 0, это означает, что одна из двух подстрок пуста, поэтому верните 0.
Если memo[m][n] != -1, это означает, что мы уже решили эту подзадачу, поэтому верните memo[m][n].
Если последние символы подстрок совпадают, добавьте 1 и найдите длину наибольшей общей подпоследовательности, исключив последний символ обеих подстрок. Верните memo[i][j] = 1 + lcs(s1, s2, m - 1, n - 1, memo).
В противном случае, если последние символы не совпадают, рекурсивно найдите наибольшую общую подпоследовательность в обеих подстроках, исключив их последние символы по одному. Верните memo[i][j] = max(lcs(s1, s2, m - 1, n, memo), lcs(s1, s2, m, n - 1, memo)).

😎 Решение
func lcs(s1, s2 string, m, n int, memo [][]int) int {
if m == 0 || n == 0 {
return 0
}
if memo[m][n] != -1 {
return memo[m][n]
}
if s1[m-1] == s2[n-1] {
memo[m][n] = 1 + lcs(s1, s2, m-1, n-1, memo)
} else {
memo[m][n] = max(lcs(s1, s2, m-1, n, memo), lcs(s1, s2, m, n-1, memo))
}
return memo[m][n]
}

func minInsertions(s string) int {
n := len(s)
sReverse := reverseString(s)
memo := make([][]int, n+1)
for i := range memo {
memo[i] = make([]int, n+1)
for j := range memo[i] {
memo[i][j] = -1
}
}
return n - lcs(s, sReverse, n, n, memo)
}

func reverseString(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}

func max(a, b int) int {
if a > b {
return a
}
return b
}


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

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

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


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

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

2⃣Храните все сериализованные представления поддеревьев в хэш-таблице и отслеживайте частоту их появления.

3⃣Найдите поддеревья, которые появляются более одного раза, и верните корневые узлы этих поддеревьев.

😎 Решение:
package main

import (
"fmt"
"strconv"
"strings"
)

type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

func findDuplicateSubtrees(root *TreeNode) []*TreeNode {
count := make(map[string]int)
result := []*TreeNode{}
serialize(root, count, &result)
return result
}

func serialize(node *TreeNode, count map[string]int, result *[]*TreeNode) string {
if node == nil {
return "#"
}
serial := strconv.Itoa(node.Val) + "," + serialize(node.Left, count, result) + "," + serialize(node.Right, count, result)
count[serial]++
if count[serial] == 2 {
*result = append(*result, node)
}
return serial
}

func main() {
root := &TreeNode{Val: 1, Left: &TreeNode{Val: 2, Left: &TreeNode{Val: 4}}, Right: &TreeNode{Val: 3, Left: &TreeNode{Val: 2, Left: &TreeNode{Val: 4}}, Right: &TreeNode{Val: 4}}}
result := findDuplicateSubtrees(root)
for _, node := range result {
fmt.Println(node.Val)
}
}


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

Вам даны два бинарных дерева root1 и root2. Представьте, что при наложении одного из них на другое некоторые узлы двух деревьев перекрываются, а другие - нет. Вам нужно объединить эти два дерева в новое бинарное дерево. Правило слияния таково: если два узла пересекаются, то в качестве нового значения объединенного узла используется сумма значений узлов. В противном случае в качестве узла нового дерева будет использоваться узел NOT null. Возвращается объединенное дерево. Примечание: Процесс объединения должен начинаться с корневых узлов обоих деревьев.

Пример:
Input: root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
Output: [3,4,5,5,4,null,7]


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

1⃣Если один из узлов пуст (null), возвращаем другой узел. Если оба узла пустые, возвращаем null.

2⃣Если оба узла не пустые, создаем новый узел, значение которого равно сумме значений двух узлов. Рекурсивно объединяем левые и правые поддеревья.

3⃣Возвращаем новый узел, который является корнем объединенного дерева.

😎 Решение:
package main

type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
if root1 == nil {
return root2
}
if root2 == nil {
return root1
}

mergedNode := &TreeNode{Val: root1.Val + root2.Val}
mergedNode.Left = mergeTrees(root1.Left, root2.Left)
mergedNode.Right = mergeTrees(root1.Right, root2.Right)

return mergedNode
}


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

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

Слово определяется как последовательность символов, не являющихся пробелами. Слова в s будут разделены одним пробелом.

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

Пример:
Input: s = ["a"]
Output: ["a"]


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

1⃣Перевернуть всю строку: применить функцию reverse, которая переворачивает весь массив символов от начала до конца.

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

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

😎 Решение:
func reverseWords(s []byte) {
reverse := func(s []byte) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}

reverse(s)
start, n := 0, len(s)
for start < n {
end := start
for end < n && s[end] != ' ' {
end++
}
reverse(s[start:end])
start = end + 1
}
}


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

Дан массив строк words (без дубликатов). Верните все составные слова из данного списка слов.

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

Пример:
Input: words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
Output: ["catsdogcats","dogcatsdog","ratcatdogcat"]
Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats";
"dogcatsdog" can be concatenated by "dog", "cats" and "dog";
"ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat".


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

1⃣Для каждого слова в списке:
Построить неявный граф, в котором узлы представляют индексы символов в слове, а ребра представляют возможность перехода от одного индекса к другому, если подстрока между ними является словом из списка.

2⃣Использовать поиск в глубину (DFS) для проверки, можно ли достигнуть узел с индексом word.length от узла с индексом 0 в графе.

3⃣Если узел word.length достижим от узла 0, добавить слово в ответ.

😎 Решение:
package main

import (
"strings"
)

type Solution struct{}

func (s Solution) dfs(word string, length int, visited []bool, dictionary map[string]bool) bool {
if length == len(word) {
return true
}
if visited[length] {
return false
}
visited[length] = true
for i := len(word) - 1; i > length; i-- {
if dictionary[word[length:i]] && s.dfs(word, i, visited, dictionary) {
return true
}
}
return false
}

func (s Solution) findAllConcatenatedWordsInADict(words []string) []string {
dictionary := make(map[string]bool)
for _, word := range words {
dictionary[word] = true
}
var answer []string
for _, word := range words {
visited := make([]bool, len(word))
if s.dfs(word, 0, visited, dictionary) {
answer = append(answer, word)
}
}
return answer
}

func main() {
solution := Solution{}
words := []string{"cat", "cats", "catsdogcats", "dog", "dogcatsdog", "hippopotamuses", "rat", "ratcatdogcat"}
result := solution.findAllConcatenatedWordsInADict(words)
for _, word := range result {
println(word)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 345. Reverse Vowels of a String
Сложность: easy

Дана строка s, переверните только все гласные в строке и верните её.

Гласные: 'a', 'e', 'i', 'o', 'u', а также их верхние регистры.

Пример:
Input: s = "hello"
Output: "holle"


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

1⃣Инициализация указателей и гласных:
Создайте набор гласных для быстрой проверки.
Установите два указателя: один на начало строки (left), другой на конец строки (right).

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

3⃣Завершение работы:
Когда указатели пересекутся, остановите процесс и верните строку.

😎 Решение:
func reverseVowels(s string) string {
vowels := "aeiouAEIOU"
chars := []rune(s)
left, right := 0, len(chars) - 1

isVowel := func(c rune) bool {
return strings.ContainsRune(vowels, c)
}

for left < right {
if !isVowel(chars[left]) {
left++
} else if !isVowel(chars[right]) {
right--
} else {
chars[left], chars[right] = chars[right], chars[left]
left++
right--
}
}

return string(chars)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1535. Find the Winner of an Array Game
Сложность: medium

Дан целочисленный массив arr из различных целых чисел и целое число k.

Игра будет проводиться между первыми двумя элементами массива (т.е. arr[0] и arr[1]). В каждом раунде игры мы сравниваем arr[0] с arr[1], большее число побеждает и остается на позиции 0, а меньшее число перемещается в конец массива. Игра заканчивается, когда одно число выигрывает k подряд раундов.

Верните число, которое выиграет игру.

Гарантируется, что у игры будет победитель.

Пример:
Input: arr = [2,1,3,5,4,6,7], k = 2
Output: 5
Explanation: Let's see the rounds of the game:
Round |       arr       | winner | win_count
  1   | [2,1,3,5,4,6,7] | 2      | 1
  2   | [2,3,5,4,6,7,1] | 3      | 1
  3   | [3,5,4,6,7,1,2] | 5      | 1
  4   | [5,4,6,7,1,2,3] | 5      | 2
So we can see that 4 rounds will be played and 5 is the winner because it wins 2 consecutive games.


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

1⃣Инициализируйте maxElement как максимальный элемент в arr, queue как очередь с элементами массива, кроме первого, curr = arr[0] и winstreak = 0.

2⃣Пока очередь не пуста (или используйте бесконечный цикл), извлеките opponent из начала очереди. Если curr > opponent, добавьте opponent в конец очереди и увеличьте winstreak на 1. В противном случае добавьте curr в конец очереди, установите curr = opponent и winstreak = 1.

3⃣Если winstreak = k или curr = maxElement, верните curr. Код никогда не должен достигать этой точки, так как гарантированно есть победитель. Верните любое значение.

😎 Решение
public class Solution {
    public int GetWinner(int[] arr, int k) {
        int maxElement = arr[0];
        Queue<int> queue = new Queue<int>();
        for (int i = 1; i < arr.Length; i++) {
            maxElement = Math.Max(maxElement, arr[i]);
            queue.Enqueue(arr[i]);
        }
       
        int curr = arr[0];
        int winstreak = 0;
       
        while (queue.Count > 0) {
            int opponent = queue.Dequeue();
           
            if (curr > opponent) {
                queue.Enqueue(opponent);
                winstreak++;
            } else {
                queue.Enqueue(curr);
                curr = opponent;
                winstreak = 1;
            }
           
            if (winstreak == k || curr == maxElement) {
                return curr;
            }
        }
       
        return -1;
    }
}


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

Если задан целочисленный массив arr и целое число target, верните количество кортежей i, j, k, таких, что i < j < k и arr[i] + arr[j] + arr[k] == target. Поскольку ответ может быть очень большим, верните его по модулю 10^9 + 7.

Пример:
Input: arr = [1,1,2,2,3,3,4,4,5,5], target = 8
Output: 20


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

1⃣Отсортировать массив arr.

2⃣Инициализировать счетчик для количества кортежей.
Пройти по массиву тремя указателями i, j, и k:
Для каждого i, установить j на i + 1, и k на конец массива.
Использовать двухуказательный метод для нахождения пар (j, k), таких что arr[i] + arr[j] + arr[k] == target.

3⃣Вернуть результат по модулю 10^9 + 7.

😎 Решение:
package main

import "sort"

func threeSumMulti(arr []int, target int) int {
sort.Ints(arr)
const MOD = 1_000_000_007
count := 0

for i := 0; i < len(arr); i++ {
j, k := i+1, len(arr)-1
for j < k {
sum := arr[i] + arr[j] + arr[k]
if sum == target {
if arr[j] == arr[k] {
count += (k - j + 1) * (k - j) / 2
break
} else {
left, right := 1, 1
for j+1 < k && arr[j] == arr[j+1] {
left++
j++
}
for k-1 > j && arr[k] == arr[k-1] {
right++
k--
}
count += left * right
j++
k--
}
} else if sum < target {
j++
} else {
k--
}
}
}

return count % MOD
}


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