C# | LeetCode
3.34K subscribers
198 photos
1 file
1.3K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.me/+nebTPWgpeGs1OWFi
Вопросы собесов t.me/+sjKGQXl79ytkYzIy
Вакансии t.me/+BQFHXZQ0zrViNGIy
Download Telegram
Задача: 646. Maximum Length of Pair Chain
Сложность: medium

Вам дан массив из n пар, где pairs[i] = [lefti, righti] и lefti < righti. Пара p2 = [c, d] следует за парой p1 = [a, b], если b < c. Таким образом можно построить цепочку пар. Верните самую длинную цепочку, которую можно составить. Вам не нужно использовать все заданные интервалы. Вы можете выбирать пары в любом порядке.

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


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

1⃣Отсортируйте пары по второму элементу каждой пары (righti).

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

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

😎 Решение:
public class Solution {
public int FindLongestChain(int[][] pairs) {
Array.Sort(pairs, (a, b) => a[1] - b[1]);
int currentEnd = int.MinValue;
int count = 0;
foreach (var pair in pairs) {
if (currentEnd < pair[0]) {
currentEnd = pair[1];
count++;
}
}
return count;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1315. Sum of Nodes with Even-Valued Grandparent
Сложность: medium

Given the root of a binary tree, return the sum of values of nodes with an even-valued grandparent. If there are no nodes with an even-valued grandparent, return 0.

A grandparent of a node is the parent of its parent if it exists.

Пример:
Input: root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5]
Output: 18
Explanation: The red nodes are the nodes with even-value grandparent while the blue nodes are the even-value grandparents.


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

1⃣Определите метод solve(), который принимает TreeNode root, значение родителя parent и значение бабушки или дедушки gParent. Этот метод возвращает сумму значений узлов с четным значением бабушки и дедушки в поддереве узла root. Если root равен null, верните 0 как сумму.

2⃣Рекурсивно пройдите по левому и правому дочерним узлам, передавая в качестве значения parent root, а в качестве значения gParent parent. Если значение gParent четное, добавьте значение root к ответу.

3⃣Вызовите рекурсивную функцию solve() с корневым узлом и значениями -1 для parent и gParent. Верните сумму для левого и правого дочерних узлов и значение для текущего узла.

😎 Решение:
public class Solution {
private int Solve(TreeNode root, int parent, int gParent) {
if (root == null) {
return 0;
}
return Solve(root.left, root.val, parent) + Solve(root.right, root.val, parent) + (gParent % 2 == 0 ? root.val : 0);
}

public int SumEvenGrandparent(TreeNode root) {
return Solve(root, -1, -1);
}
}


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

Дан массив двоичных строк strs и два целых числа m и n.

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

Множество x является подмножеством множества y, если все элементы множества x также являются элементами множества y.

Пример:
Input: strs = ["10","0001","111001","1","0"], m = 5, n = 3
Output: 4
Explanation: The largest subset with at most 5 0's and 3 1's is {"10", "0001", "1", "0"}, so the answer is 4.
Other valid but smaller subsets include {"0001", "1"} and {"10", "1", "0"}.
{"111001"} is an invalid subset because it contains 4 1's, greater than the maximum of 3.


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

1⃣Рассматриваем все возможные подмножества, прерывая цикл, если количество нулей превышает m или количество единиц превышает n.

2⃣Считаем количество нулей и единиц в каждом подмножестве.

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

😎 Решение:
public class Solution {
public int FindMaxForm(string[] strs, int m, int n) {
int maxlen = 0;
for (int i = 0; i < (1 << strs.Length); i++) {
int zeroes = 0, ones = 0, len = 0;
for (int j = 0; j < 32; j++) {
if ((i & (1 << j)) != 0) {
int[] count = CountZeroesOnes(strs[j]);
zeroes += count[0];
ones += count[1];
if (zeroes > m || ones > n)
break;
len++;
}
}
if (zeroes <= m && ones <= n)
maxlen = Math.Max(maxlen, len);
}
return maxlen;
}

public int[] CountZeroesOnes(string s) {
int[] c = new int[2];
foreach (char ch in s) {
c[ch - '0']++;
}
return c;
}
}


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

В английском языке есть понятие "корень", за которым может следовать какое-то другое слово, чтобы образовать другое более длинное слово - назовем это слово производным. Например, если за корнем "help" следует слово "ful", мы можем образовать производное "helpful". Дайте словарь, состоящий из множества корней, и предложение, состоящее из слов, разделенных пробелами, замените все производные в предложении на образующий их корень. Если производное может быть заменено более чем одним корнем, замените его корнем, имеющим наименьшую длину. Верните предложение после замены.

Пример:
Input: dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
Output: "the cat was rat by the bat"


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

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

2⃣Пройдите по каждому слову в предложении и найдите самый короткий корень, который является префиксом этого слова.

3⃣Замените слово найденным корнем и соберите обновленное предложение.

😎 Решение:
using System;
using System.Collections.Generic;
using System.Linq;

public class Solution {
public string ReplaceWords(IList<string> roots, string sentence) {
var rootSet = new HashSet<string>(roots);

string Replace(string word) {
for (int i = 1; i <= word.Length; i++) {
string prefix = word.Substring(0, i);
if (rootSet.Contains(prefix)) {
return prefix;
}
}
return word;
}

return string.Join(" ", sentence.Split(' ').Select(Replace));
}
}


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

Если задан корень бинарного дерева, верните сумму всех левых листьев. Лист - это узел, не имеющий детей. Левый лист - это лист, который является левым ребенком другого узла.

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


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

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

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

3⃣Рекурсивный вызов для детей
Рекурсивно вызовите функцию для левого и правого детей текущего узла, передавая соответствующий флаг.

😎 Решение:
public class Solution {
public int SumOfLeftLeaves(TreeNode root) {
return Dfs(root, false);
}

private int Dfs(TreeNode node, bool isLeft) {
if (node == null) return 0;
if (node.left == null && node.right == null) return isLeft ? node.val : 0;
return Dfs(node.left, true) + Dfs(node.right, false);
}
}


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

Сообщение, содержащее буквы от A до Z, можно закодировать в числа с использованием следующего соответствия:

- 'A' -> "1"
- 'B' -> "2"
- ...
- 'Z' -> "26"

Для декодирования закодированного сообщения все цифры должны быть сгруппированы и затем отображены обратно в буквы с использованием обратного соответствия (существует несколько способов). Например, "11106" можно представить как:

- "AAJF" с группировкой (1 1 10 6)
- "KJF" с группировкой (11 10 6)

Обратите внимание, что группировка (1 11 06) недопустима, потому что "06" не может быть преобразовано в 'F', так как "6" отличается от "06".

Для данной строки s, содержащей только цифры, верните количество способов декодирования.

Тестовые случаи сформированы таким образом, что ответ укладывается в 32-битное целое число.

Пример:
Input: s = "12"
Output: 2
Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).


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

1⃣Входим в рекурсию с данной строкой, начиная с индекса 0.

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

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

😎 Решение:
public class Solution {
private bool valid(string s, int start, int length) {
return length == 1 ||
(s[start] != '0' &&
(length < 3 || int.Parse(s.Substring(start, length)) <= 255));
}

private void helper(string s, int startIndex, List<int> dots,
List<string> ans) {
var remainingLength = s.Length - startIndex;
var remainingNumberOfIntegers = 4 - dots.Count;
if (remainingLength > remainingNumberOfIntegers * 3 ||
remainingLength < remainingNumberOfIntegers)
return;
if (dots.Count == 3) {
if (valid(s, startIndex, remainingLength)) {
var temp = "";
var last = 0;
foreach (var dot in dots) {
temp += s.Substring(last, dot) + ".";
last += dot;
}

temp += s.Substring(startIndex);
ans.Add(temp);
}

return;
}

for (int curPos = 1; curPos <= 3 && curPos <= remainingLength;
++curPos) {
dots.Add(curPos);
if (valid(s, startIndex, curPos)) {
helper(s, startIndex + curPos, dots, ans);
}

dots.RemoveAt(dots.Count - 1);
}
}

public IList<string> RestoreIpAddresses(string s) {
var ans = new List<string>();
helper(s, 0, new List<int>(), ans);
return ans;
}
}


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

Дана строка s. Верните количество подстрок, которые содержат только одну уникальную букву.

Пример:
Input: s = "aaaba"
Output: 8
Explanation: The substrings with one distinct letter are "aaa", "aa", "a", "b".
"aaa" occurs 1 time.
"aa" occurs 2 times.
"a" occurs 4 times.
"b" occurs 1 time.
So the answer is 1 + 2 + 4 + 1 = 8.


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

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

2⃣Итерируйте по строке S: Если мы не достигли конца и новый символ S[right] такой же, как и начальный символ S[left], увеличьте right на 1 для дальнейшего исследования строки S; в противном случае вычислите длину подстроки как right - left и примените формулу суммы арифметической последовательности; не забудьте установить right в left для начала исследования новой подстроки.

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

😎 Решение:
public class Solution {
public int CountLetters(string S) {
int total = 0;
int left = 0;

for (int right = 0; right <= S.Length; right++) {
if (right == S.Length || S[left] != S[right]) {
int lenSubstring = right - left;
total += (1 + lenSubstring) * lenSubstring / 2;
left = right;
}
}
return total;
}
}


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

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

Пример:
Input: products = ["havana"], searchWord = "havana"
Output: [["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]


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

1⃣Отсортируйте массив продуктов.

2⃣Итерируйтесь по каждому символу в searchWord, находите все продукты, которые соответствуют текущему префиксу.

3⃣Сохраняйте не более трех лексикографически минимальных продуктов для каждого префикса.

😎 Решение:
using System;
using System.Collections.Generic;
using System.Linq;

public class Solution {
public IList<IList<string>> SuggestedProducts(string[] products, string searchWord) {
Array.Sort(products);
var result = new List<IList<string>>();
var prefix = "";
foreach (var c in searchWord) {
prefix += c;
var suggestions = products.Where(p => p.StartsWith(prefix)).Take(3).ToList();
result.Add(suggestions);
}
return result;
}
}


Ставь 👍 и забирай 📚 Базу знаний
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.


😎 Решение:
public class Solution {
public IList<IList<int>> GroupThePeople(int[] groupSizes) {
var ans = new List<IList<int>>();
var szToGroup = new Dictionary<int, List<int>>();

for (int i = 0; i < groupSizes.Length; i++) {
int size = groupSizes[i];
if (!szToGroup.ContainsKey(size)) {
szToGroup[size] = new List<int>();
}
szToGroup[size].Add(i);

if (szToGroup[size].Count == size) {
ans.Add(new List<int>(szToGroup[size]));
szToGroup[size].Clear();
}
}

return ans;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 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.

😎 Решение:
public class Solution {
public TreeNode RemoveLeafNodes(TreeNode root, int target) {
if (root == null) {
return null;
}

root.left = RemoveLeafNodes(root.left, target);
root.right = RemoveLeafNodes(root.right, target);

if (root.left == null && root.right == null && root.val == target) {
return null;
}
return root;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 164. Maximum Gap
Сложность: medium

Дан массив целых чисел nums. Верните максимальную разницу между двумя последовательными элементами в его отсортированной форме. Если массив содержит менее двух элементов, верните 0.

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

Пример:
Input: nums = [3,6,9,1]
Output: 3
Explanation: The sorted form of the array is [1,3,6,9], either (3,6) or (6,9) has the maximum difference 3.


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

1⃣Инициализация:
Определите минимальное и максимальное значения в массиве для расчета возможного максимального интервала (разрыва) между элементами в идеально распределенном массиве.
Вычислите размер ведра (bucket size), необходимый для размещения всех элементов массива так, чтобы если массив был равномерно распределен, каждый ведер должен содержать хотя бы один элемент. Размер ведра = (max_value - min_value) / (количество элементов - 1).

2⃣Размещение элементов в ведрах:
Создайте ведра для хранения минимальных и максимальных значений каждого ведра. Используйте формулу для распределения каждого элемента в соответствующем ведре на основе его значения.
Игнорируйте пустые ведра при расчете максимального интервала.

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

😎 Решение:
public class Solution {
public int MaximumGap(int[] nums) {
if (nums == null ||
nums.Length < 2)
return 0;

Array.Sort(nums);

int maxGap = 0;

for (int i = 0; i < nums.Length - 1; i++)
maxGap = Math.Max(nums[i + 1] - nums[i], maxGap);

return maxGap;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 715. Range Module
Сложность: hard

Модуль Range - это модуль, который отслеживает диапазоны чисел. Создайте структуру данных для отслеживания диапазонов, представленных в виде полуоткрытых интервалов, и запросов к ним. Полуоткрытый интервал [left, right) обозначает все вещественные числа x, где left <= x < right. Реализуйте класс RangeModule: RangeModule() Инициализирует объект структуры данных. void addRange(int left, int right) Добавляет полуоткрытый интервал [left, right), отслеживая каждое вещественное число в этом интервале. Добавление интервала, который частично перекрывает отслеживаемые в данный момент числа, должно добавить все числа в интервале [left, right), которые еще не отслеживаются. boolean queryRange(int left, int right) Возвращает true, если каждое действительное число в интервале [left, right) отслеживается в данный момент, и false в противном случае. void removeRange(int left, int right) Прекращает отслеживание каждого действительного числа, отслеживаемого в данный момент в полуоткрытом интервале [left, right).

Пример:
Input
["RangeModule", "addRange", "removeRange", "queryRange", "queryRange", "queryRange"]
[[], [10, 20], [14, 16], [10, 14], [13, 15], [16, 17]]
Output
[null, null, null, true, false, true]


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

1⃣Инициализируйте класс RangeModule с пустым списком диапазонов.

2⃣Для метода addRange(left, right) добавьте новый диапазон, объединяя его с существующими перекрывающимися диапазонами. Для метода queryRange(left, right) проверьте, полностью ли данный диапазон содержится в отслеживаемых диапазонах.

3⃣Для метода removeRange(left, right) удалите указанный диапазон, разбивая существующие диапазоны на соответствующие части.

😎 Решение:
public class RangeModule {

private List<(int, int)> ranges;

public RangeModule() {
ranges = new List<(int, int)>();
}

public void AddRange(int left, int right) {
var newRanges = new List<(int, int)>();
int i = 0;
while (i < ranges.Count && ranges[i].Item2 < left) {
newRanges.Add(ranges[i]);
i++;
}
while (i < ranges.Count && ranges[i].Item1 <= right) {
left = Math.Min(left, ranges[i].Item1);
right = Math.Max(right, ranges[i].Item2);
i++;
}
newRanges.Add((left, right));
while (i < ranges.Count) {
newRanges.Add(ranges[i]);
i++;
}
ranges = newRanges;
}

public bool QueryRange(int left, int right) {
foreach (var range in ranges) {
if (range.Item1 <= left && right <= range.Item2) {
return true;
}
}
return false;
}

public void RemoveRange(int left, int right) {
var newRanges = new List<(int, int)>();
foreach (var range in ranges) {
if (range.Item1 < left) {
newRanges.Add((range.Item1, Math.Min(range.Item2, left)));
}
if (right < range.Item2) {
newRanges.Add((Math.Max(range.Item1, right), range.Item2)));
}
}
ranges = newRanges;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 641. Design Circular Deque
Сложность: medium

Разработайте свою реализацию круговой двусторонней очереди (deque). Реализуйте класс MyCircularDeque: MyCircularDeque(int k) Инициализирует deque с максимальным размером k. boolean insertFront() Добавляет элемент в переднюю часть Deque. Возвращает true, если операция прошла успешно, или false в противном случае. boolean insertLast() Добавляет элемент в заднюю часть Deque. Возвращает true, если операция выполнена успешно, или false в противном случае. boolean deleteFront() Удаляет элемент из передней части Deque. Возвращает true, если операция прошла успешно, или false в противном случае. boolean deleteLast() Удаляет элемент из задней части Deque. Возвращает true, если операция прошла успешно, или false в противном случае. int getFront() Возвращает передний элемент из Deque. Возвращает -1, если Deque пуст. int getRear() Возвращает последний элемент из Deque. Возвращает -1, если Deque пуст. boolean isEmpty() Возвращает true, если Deque пуст, или false в противном случае. boolean isFull() Возвращает true, если Deque полон, или false в противном случае.

Пример:
Input
["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"]
[[3], [1], [2], [3], [4], [], [], [], [4], []]
Output
[null, true, true, true, false, 2, true, true, true, 4]


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

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

2⃣Операции вставки
Реализуйте методы вставки элементов в переднюю и заднюю части очереди с учетом кольцевой структуры.

3⃣Операции удаления
Реализуйте методы удаления элементов из передней и задней частей очереди с учетом кольцевой структуры и методы для получения переднего и заднего элементов очереди.

😎 Решение:
public class MyCircularDeque {
private int[] deque;
private int front;
private int rear;
private int size;
private int capacity;

public MyCircularDeque(int k) {
capacity = k;
deque = new int[k];
front = 0;
rear = 0;
size = 0;
}

public bool InsertFront(int value) {
if (IsFull()) return false;
front = (front - 1 + capacity) % capacity;
deque[front] = value;
size++;
return true;
}

public bool InsertLast(int value) {
if (IsFull()) return false;
deque[rear] = value;
rear = (rear + 1) % capacity;
size++;
return true;
}

public bool DeleteFront() {
if (IsEmpty()) return false;
front = (front + 1) % capacity;
size--;
return true;
}

public bool DeleteLast() {
if (IsEmpty()) return false;
rear = (rear - 1 + capacity) % capacity;
size--;
return true;
}

public int GetFront() {
if (IsEmpty()) return -1;
return deque[front];
}

public int GetRear() {
if (IsEmpty()) return -1;
return deque[(rear - 1 + capacity) % capacity];
}

public bool IsEmpty() {
return size == 0;
}

public bool IsFull() {
return size == capacity;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 996. Number of Squareful Arrays
Сложность: hard

Массив является квадратным, если сумма каждой пары соседних элементов является совершенным квадратом. Если задан целочисленный массив nums, верните количество перестановок nums, которые являются квадратными. Две перестановки perm1 и perm2 различны, если существует некоторый индекс i такой, что perm1[i] != perm2[i].

Пример:
Input: nums = [1,17,8]
Output: 2


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

1⃣Генерация перестановок:
Сгенерируйте все возможные перестановки массива nums.
Для каждой перестановки проверьте, является ли она квадратной.

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

3⃣Подсчет квадратных перестановок:
Подсчитайте количество перестановок, которые являются квадратными, и верните это значение.

😎 Решение:
public class Solution {
public int NumSquarefulPerms(int[] nums) {
Array.Sort(nums);
var used = new bool[nums.Length];
var result = new HashSet<string>();
var path = new List<int>();
Backtrack(nums, used, path, result);
return result.Count;
}

private void Backtrack(int[] nums, bool[] used, List<int> path, HashSet<string> result) {
if (path.Count == nums.Length) {
if (IsSquareful(path)) {
result.Add(string.Join(",", path));
}
return;
}

for (int i = 0; i < nums.Length; i++) {
if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])) continue;
path.Add(nums[i]);
used[i] = true;
Backtrack(nums, used, path, result);
path.RemoveAt(path.Count - 1);
used[i] = false;
}
}

private bool IsSquareful(List<int> perm) {
for (int i = 0; i < perm.Count - 1; i++) {
int sum = perm[i] + perm[i + 1];
int root = (int)Math.Sqrt(sum);
if (root * root != sum) return false;
}
return true;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 58. Length of Last Word
Сложность: easy

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

Пример:
Input: s = "Hello World"  
Output: 5
Explanation: Последнее слово — "World", его длина 5.


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

1⃣Пройти строку с конца, игнорируя пробелы.

2⃣Найдя последнее слово, считать его длину.

3⃣Вернуть длину последнего слова.

😎 Решение:
public class Solution {
public int LengthOfLastWord(string s) {
int length = 0, i = s.Length - 1;

while (i >= 0 && s[i] == ' ') i--; // Пропуск пробелов
while (i >= 0 && s[i] != ' ') { // Подсчет длины слова
length++;
i--;
}

return length;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 258. Add Digits
Сложность: easy

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

Пример:
Input: num = 38
Output: 2
Explanation: The process is
38 --> 3 + 8 --> 11
11 --> 1 + 1 --> 2
Since 2 has only one digit, return it.


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

1⃣Инициализируйте переменную digital_root значением 0.

2⃣В цикле, пока num больше 0:
Добавьте к digital_root последнюю цифру num.
Уменьшите num, удалив последнюю цифру.
Если num равно 0 и digital_root больше 9, присвойте num значение digital_root и сбросьте digital_root в 0.

3⃣Верните значение digital_root.

😎 Решение:
public class Solution {
public int AddDigits(int num) {
int digital_root = 0;
while (num > 0) {
digital_root += num % 10;
num /= 10;
if (num == 0 && digital_root > 9) {
num = digital_root;
digital_root = 0;
}
}
return digital_root;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 247. Strobogrammatic Number II
Сложность: medium

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

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

Пример:
Input: n = 2
Output: ["11","69","88","96"]


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

1⃣Инициализируйте структуру данных reversiblePairs, которая содержит все пары обратимых цифр. Вызовите и верните результат рекурсивной функции generateStroboNumbers(n, finalLength), где первый аргумент указывает, что текущий вызов создаст все стробограмматические числа длиной n, а второй аргумент указывает длину конечных стробограмматических чисел, которые мы будем генерировать, и будет использоваться для проверки возможности добавления '0' в начало и конец числа.

2⃣Создайте функцию generateStroboNumbers(n, finalLength), которая вернет все стробограмматические числа длиной n:
Проверьте базовые случаи: если n == 0, верните массив с пустой строкой [""]; если n == 1, верните ["0", "1", "8"].
Вызовите generateStroboNumbers(n - 2, finalLength), чтобы получить все стробограмматические числа длиной (n-2), и сохраните их в subAns.
Инициализируйте пустой массив currStroboNums для хранения стробограмматических чисел длиной n.

3⃣Для каждого числа в subAns добавьте все reversiblePairs в начало и конец, за исключением случая, когда текущая пара '00' и n == finalLength (потому что нельзя добавить '0' в начало числа), и добавьте это новое число в currStroboNums. В конце функции верните все стробограмматические числа, т.е. currStroboNums.

😎 Решение:
using System;
using System.Collections.Generic;
using System.Text;

public class Solution {
private List<List<char>> reversiblePairs = new List<List<char>> {
new List<char>{'0', '0'}, new List<char>{'1', '1'},
new List<char>{'6', '9'}, new List<char>{'8', '8'}, new List<char>{'9', '6'}
};

public List<string> GenerateStroboNumbers(int n, int finalLength) {
if (n == 0) {
return new List<string> { "" };
}

if (n == 1) {
return new List<string> { "0", "1", "8" };
}

List<string> prevStroboNums = GenerateStroboNumbers(n - 2, finalLength);
List<string> currStroboNums = new List<string>();

foreach (string prevStroboNum in prevStroboNums) {
foreach (List<char> pair in reversiblePairs) {
if (pair[0] != '0' || n != finalLength) {
currStroboNums.Add(pair[0] + prevStroboNum + pair[1]);
}
}
}

return currStroboNums;
}

public List<string> FindStrobogrammatic(int n) {
return GenerateStroboNumbers(n, n);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 916. Word Subsets
Сложность: medium

Вам даны два массива строк words1 и words2. Строка b является подмножеством строки a, если каждая буква в b встречается в ней, включая кратность. Например, "wrr" является подмножеством "warrior", но не является подмножеством "world". Строка a из words1 является универсальной, если для каждой строки b в words2, b является подмножеством a. Верните массив всех универсальных строк в words1. Вы можете вернуть ответ в любом порядке.

Пример:
Input: words1 = ["amazon","apple","facebook","google","leetcode"], words2 = ["e","o"]
Output: ["facebook","google","leetcode"]


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

1⃣Подсчитать максимальное количество каждой буквы в каждом слове из words2.

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

3⃣Вернуть массив слов из words1, которые удовлетворяют этому условию.

😎 Решение:
using System;
using System.Collections.Generic;
using System.Linq;

public class Solution {
public IList<string> WordSubsets(string[] words1, string[] words2) {
int[] maxCount = new int[26];
foreach (string word in words2) {
int[] count = GetCount(word);
for (int i = 0; i < 26; i++) {
maxCount[i] = Math.Max(maxCount[i], count[i]);
}
}

List<string> result = new List<string>();
foreach (string word in words1) {
int[] count = GetCount(word);
if (IsUniversal(count, maxCount)) {
result.Add(word);
}
}

return result;
}

private int[] GetCount(string word) {
int[] count = new int[26];
foreach (char c in word) {
count[c - 'a']++;
}
return count;
}

private bool IsUniversal(int[] count, int[] maxCount) {
for (int i = 0; i < 26; i++) {
if (count[i] < maxCount[i]) {
return false;
}
}
return true;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 553. Optimal Division
Сложность: medium

Дано целочисленный массив nums. Соседние целые числа в nums будут выполнять деление с плавающей запятой.

Например, для nums = [2,3,4] мы будем вычислять выражение "2/3/4".
Однако, вы можете добавить любое количество скобок в любое место, чтобы изменить приоритет операций. Вы хотите добавить эти скобки так, чтобы значение выражения после вычисления было максимальным.

Верните соответствующее выражение, которое имеет максимальное значение в строковом формате.

Пример:
Input: nums = [1000,100,10,2]
Output: "1000/(100/10/2)"
Explanation: 1000/(100/10/2) = 1000/((100/10)/2) = 200
However, the bold parenthesis in "1000/((100/10)/2)" are redundant since they do not influence the operation priority.
So you should return "1000/(100/10/2)".
Other cases:
1000/(100/10)/2 = 50
1000/(100/(10/2)) = 50
1000/100/10/2 = 0.5
1000/100/(10/2) = 2


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

1⃣Разверните оба числа. Инициализируйте массив ans с (N+M) нулями. Для каждой цифры во втором числе: держите переменную переноса, изначально равную 0. Инициализируйте массив (currentResult), начинающийся с некоторых нулей в зависимости от места цифры во втором числе.

2⃣Для каждой цифры первого числа: умножьте цифру второго числа на цифру первого числа и добавьте предыдущий перенос к результату умножения. Возьмите остаток от деления на 10, чтобы получить последнюю цифру. Добавьте последнюю цифру к массиву currentResult. Разделите результат умножения на 10, чтобы получить новое значение переноса.

3⃣После итерации по каждой цифре в первом числе, если перенос не равен нулю, добавьте перенос к массиву currentResult. Добавьте currentResult к массиву ans. Если последняя цифра в ans равна нулю, перед разворотом ans удалите этот ноль, чтобы избежать ведущего нуля в окончательном ответе. Разверните ans и верните его.

😎 Решение:
using System;
using System.Collections.Generic;
using System.Linq;

public class Solution {
private List<int> AddStrings(List<int> num1, List<int> num2) {
var ans = new List<int>();
int carry = 0;
int n1 = num1.Count;
int n2 = num2.Count;

for (int i = 0; i < Math.Max(n1, n2) + 1; ++i) {
int digit1 = i < n1 ? num1[i] : 0;
int digit2 = i < n2 ? num2[i] : 0;
int sum = digit1 + digit2 + carry;
carry = sum / 10;
ans.Add(sum % 10);
}
return ans;
}

private List<int> MultiplyOneDigit(string firstNumber, char secondNumberDigit, int numZeros) {
var currentResult = new List<int>(new int[numZeros]);
int carry = 0;

foreach (char digit in firstNumber) {
int multiplication = (secondNumberDigit - '0') * (digit - '0') + carry;
carry = multiplication / 10;
currentResult.Add(multiplication % 10);
}
if (carry != 0) {
currentResult.Add(carry);
}
return currentResult;
}

public string Multiply(string firstNumber, string secondNumber) {
if (firstNumber == "0" || secondNumber == "0") {
return "0";
}

firstNumber = new string(firstNumber.Reverse().ToArray());
secondNumber = new string(secondNumber.Reverse().ToArray());

var ans = new List<int>(new int[firstNumber.Length + secondNumber.Length]);

for (int i = 0; i < secondNumber.Length; ++i) {
ans = AddStrings(MultiplyOneDigit(firstNumber, secondNumber[i], i), ans);
}

while (ans.Last() == 0) {
ans.RemoveAt(ans.Count - 1);
}

var answer = new System.Text.StringBuilder();
for (int i = ans.Count - 1; i >= 0; --i) {
answer.Append(ans[i]);
}

return answer.ToString();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 1114. Print in Order
Сложность: easy

Предположим, у нас есть класс:
public class Foo {
public void first() { print("first"); }
public void second() { print("second"); }
public void third() { print("third"); }
}

Один и тот же экземпляр Foo будет передан трем разным потокам. Поток A вызовет first(), поток B вызовет second(), и поток C вызовет third(). Спроектируйте механизм и модифицируйте программу, чтобы гарантировать, что second() выполняется после first(), а third() выполняется после second().

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

Пример:
Input: nums = [1,2,3]
Output: "firstsecondthird"
Explanation: There are three threads being fired asynchronously. The input [1,2,3] means thread A calls first(), thread B calls second(), and thread C calls third(). "firstsecondthird" is the correct output.


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

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

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

3⃣Функции second() и third():
В функции second() проверьте статус firstJobDone. Если она не обновлена, подождите, иначе переходите к выполнению второго задания. В конце функции обновите переменную secondJobDone, чтобы отметить завершение второго задания.
В функции third() проверьте статус secondJobDone. Аналогично функции second(), подождите сигнала secondJobDone перед тем, как приступить к выполнению третьего задания.

😎 Решение:
using System;
using System.Threading;

public class Foo {
private SemaphoreSlim firstJobDone = new SemaphoreSlim(0, 1);
private SemaphoreSlim secondJobDone = new SemaphoreSlim(0, 1);

public void First(Action printFirst) {
printFirst();
firstJobDone.Release();
}

public void Second(Action printSecond) {
firstJobDone.Wait();
printSecond();
secondJobDone.Release();
}

public void Third(Action printThird) {
secondJobDone.Wait();
printThird();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 1361. Validate Binary Tree Nodes
Сложность: easy

У вас есть n узлов бинарного дерева, пронумерованных от 0 до n-1, где узел i имеет двух детей: leftChild[i] и rightChild[i]. Верните true, если и только если все заданные узлы образуют ровно одно допустимое бинарное дерево.

Если у узла i нет левого ребенка, то leftChild[i] будет равен -1, аналогично для правого ребенка.

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

Пример:
Input: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1]
Output: true


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

1⃣Проверка количества родителей для каждого узла:
Создайте массив для отслеживания количества родителей для каждого узла. Проходите через leftChild и rightChild, увеличивая счетчик для каждого ребенка. Если какой-либо узел имеет более одного родителя, возвращайте false.

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

3⃣Проверка на достижение всех узлов:
Проверьте, что количество посещенных узлов равно n. Если нет, верните false. В противном случае, верните true.
😎 Решение:
using System;
using System.Collections.Generic;

public class Solution {
public bool ValidateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
int[] parents = new int[n];

for (int i = 0; i < n; i++) {
if (leftChild[i] != -1) {
if (++parents[leftChild[i]] > 1) {
return false;
}
}
if (rightChild[i] != -1) {
if (++parents[rightChild[i]] > 1) {
return false;
}
}
}

int root = -1;
for (int i = 0; i < n; i++) {
if (parents[i] == 0) {
if (root == -1) {
root = i;
} else {
return false;
}
}
}

if (root == -1) {
return false;
}

HashSet<int> visited = new HashSet<int>();
Queue<int> queue = new Queue<int>();
queue.Enqueue(root);

while (queue.Count > 0) {
int node = queue.Dequeue();
if (!visited.Add(node)) {
return false;
}
if (leftChild[node] != -1) {
queue.Enqueue(leftChild[node]);
}
if (rightChild[node] != -1) {
queue.Enqueue(rightChild[node]);
}
}

return visited.Count == n;
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍1