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

Тесты t.iss.one/+nebTPWgpeGs1OWFi
Вопросы собесов t.iss.one/+sjKGQXl79ytkYzIy
Вакансии t.iss.one/+BQFHXZQ0zrViNGIy
Download Telegram
Задача: 918. Maximum Sum Circular Subarray
Сложность: medium

Если задан круговой целочисленный массив nums длины n, верните максимально возможную сумму непустого подмассива nums. Круговой массив означает, что конец массива соединяется с его началом. Формально, следующий элемент nums[i] равен nums[(i + 1) % n], а предыдущий элемент nums[i] равен nums[(i - 1 + n) % n]. Подмассив может включать каждый элемент фиксированного буфера nums не более одного раза. Формально, для подмассива nums[i], nums[i + 1], ..., nums[j] не существует i <= k1, k2 <= j, при этом k1 % n == k2 % n.

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


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

1⃣Найти стандартную максимальную сумму подмассива с помощью алгоритма Кадане.

2⃣Найти минимальную сумму подмассива с помощью алгоритма Кадане и вычесть ее из общей суммы массива.

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

😎 Решение:
public class Solution {
public int MaxSubarraySumCircular(int[] nums) {
int Kadane(int[] arr) {
int currentSum = arr[0], maxSum = arr[0];
for (int i = 1; i < arr.Length; i++) {
currentSum = Math.Max(arr[i], currentSum + arr[i]);
maxSum = Math.Max(maxSum, currentSum);
}
return maxSum;
}

int maxKadane = Kadane(nums);
int totalSum = nums.Sum();
int minKadane = Kadane(nums.Select(num => -num).ToArray());

return Math.Max(maxKadane, totalSum + minKadane == 0 ? maxKadane : totalSum + minKadane);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Задача: 562. Longest Line of Consecutive One in Matrix
Сложность: medium

Дана бинарная матрица размера m x n. Вернуть длину самой длинной последовательной линии из единиц в матрице.

Линия может быть горизонтальной, вертикальной, диагональной или анти-диагональной.

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


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

1⃣Создайте 3 матрицы для хранения текущей длины последовательных единиц: horizontal, vertical, diagonal и anti_diagonal.

2⃣Итерируйте через каждый элемент матрицы: Если элемент равен 1, обновите соответствующие значения в матрицах horizontal, vertical, diagonal и anti_diagonal. Обновите максимальную длину последовательной линии.

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

😎 Решение:
public class Solution {
public int LongestLine(int[][] mat) {
if (mat == null || mat.Length == 0 || mat[0].Length == 0) {
return 0;
}

int m = mat.Length;
int n = mat[0].Length;
int maxLength = 0;

int[,] horizontal = new int[m, n];
int[,] vertical = new int[m, n];
int[,] diagonal = new int[m, n];
int[,] antiDiagonal = new int[m, n];

for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (mat[i][j] == 1) {
horizontal[i, j] = (j > 0 ? horizontal[i, j-1] : 0) + 1;
vertical[i, j] = (i > 0 ? vertical[i-1, j] : 0) + 1;
diagonal[i, j] = (i > 0 && j > 0 ? diagonal[i-1, j-1] : 0) + 1;
antiDiagonal[i, j] = (i > 0 && j < n - 1 ? antiDiagonal[i-1, j+1] : 0) + 1;

maxLength = Math.Max(maxLength, horizontal[i, j]);
maxLength = Math.Max(maxLength, vertical[i, j]);
maxLength = Math.Max(maxLength, diagonal[i, j]);
maxLength = Math.Max(maxLength, antiDiagonal[i, j]);
}
}
}

return maxLength;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 1013. Partition Array Into Three Parts With Equal Sum
Сложность: easy

Если задан массив целых чисел arr, верните true, если мы можем разбить массив на три непустые части с равными суммами. Формально, мы можем разбить массив, если можем найти индексы i + 1 < j с (arr[0] + arr[1] + ... + arr[i] == arr[i + 1] + arr[i + 2] + ... + arr[j - 1] == arr[j] + arr[j + 1] + ... + arr[arr.length - 1])

Пример:
Input: arr = [0,2,1,-6,6,-7,9,1,2,0,1]
Output: true


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

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

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

3⃣Проверка третьей части:
Убедитесь, что оставшаяся часть массива также имеет ту же сумму, что и две найденные части. Если да, вернуть true, иначе false.

😎 Решение:
public class Solution {
public bool CanThreePartsEqualSum(int[] arr) {
int totalSum = arr.Sum();
if (totalSum % 3 != 0) {
return false;
}

int target = totalSum / 3, partSum = 0, count = 0, n = arr.Length;

for (int i = 0; i < n; i++) {
partSum += arr[i];
if (partSum == target) {
count++;
partSum = 0;
if (count == 2 && i < n - 1) {
return true;
}
}
}

return false;
}
}


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

Даны две строки s1 и s2. Верните true, если s2 содержит перестановку s1, или false в противном случае.

Другими словами, верните true, если одна из перестановок s1 является подстрокой s2.

Пример:
Input: s1 = "ab", s2 = "eidbaooo"
Output: true
Explanation: s2 contains one permutation of s1 ("ba").


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

1⃣Создать массив для подсчета символов в строке s1. Затем создать аналогичный массив для первых len(s1) символов строки s2.

2⃣Использовать скользящее окно для перемещения по строке s2. Для каждой позиции окна обновлять массив подсчета символов и сравнивать его с массивом для строки s1.

3⃣Если массивы совпадают на любом этапе, вернуть true. Если окно достигает конца строки s2 и совпадений не найдено, вернуть false.

😎 Решение:
public class Solution {
public bool CheckInclusion(string s1, string s2) {
int s1Len = s1.Length, s2Len = s2.Length;
if (s1Len > s2Len) return false;

int[] s1Count = new int[26];
int[] s2Count = new int[26];

for (int i = 0; i < s1Len; i++) {
s1Count[s1[i] - 'a']++;
s2Count[s2[i] - 'a']++;
}

for (int i = 0; i < s2Len - s1Len; i++) {
if (s1Count.SequenceEqual(s2Count)) return true;
s2Count[s2[i] - 'a']--;
s2Count[s2[i + s1Len] - 'a']++;
}

return s1Count.SequenceEqual(s2Count);
}
}


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

Вам дан целочисленный массив values, в котором values[i] представляет собой значение i-й достопримечательности. Две достопримечательности i и j имеют расстояние j - i между собой. Оценка пары (i < j) достопримечательностей равна values[i] + values[j] + i - j: сумма значений достопримечательностей минус расстояние между ними. Возвращается максимальная оценка пары достопримечательностей.

Пример:
Input: values = [8,1,5,2,6]
Output: 11


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

1⃣Инициализация переменных:
Инициализируйте переменную max_score для хранения максимальной оценки пары.
Инициализируйте переменную max_i_plus_value для хранения максимального значения выражения values[i] + i при проходе по массиву.

2⃣Проход по массиву:
Пройдитесь по массиву начиная с первого элемента и для каждого элемента values[j] вычислите текущую оценку пары как values[j] - j + max_i_plus_value.
Обновите значение max_score, если текущая оценка больше.
Обновите значение max_i_plus_value, если текущий элемент values[j] + j больше предыдущего max_i_plus_value.

3⃣Возврат результата:
Верните значение max_score как максимальную оценку пары достопримечательностей.

😎 Решение:
public class Solution {
public int MaxScoreSightseeingPair(int[] values) {
int maxScore = 0;
int maxIPlusValue = values[0];

for (int j = 1; j < values.Length; j++) {
maxScore = Math.Max(maxScore, maxIPlusValue + values[j] - j);
maxIPlusValue = Math.Max(maxIPlusValue, values[j] + j);
}

return maxScore;
}
}


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

Вам дан массив целых чисел nums и два целых числа indexDiff и valueDiff.

Найдите пару индексов (i, j) таких, что:
i != j,
abs(i - j) <= indexDiff,
abs(nums[i] - nums[j]) <= valueDiff.

Верните true, если такая пара существует, или false в противном случае.

Пример:
Input: nums = [1,2,3,1], indexDiff = 3, valueDiff = 0
Output: true
Explanation: We can choose (i, j) = (0, 3).
We satisfy the three conditions:
i != j --> 0 != 3
abs(i - j) <= indexDiff --> abs(0 - 3) <= 3
abs(nums[i] - nums[j]) <= valueDiff --> abs(1 - 1) <= 0


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

1⃣Инициализация и вычисление корзин:
Рассчитать ширину корзины w = t + 1.
Инициализировать пустой хэш-таблицей buckets.
Определить функцию getID, которая возвращает идентификатор корзины для элемента x и ширины корзины w.

2⃣Итерация и проверка корзин:
Перебрать все элементы массива nums.
Для каждого элемента nums[i]:
Определить его корзину с помощью getID.
Проверить, есть ли в текущей корзине элемент. Если есть, вернуть true.
Проверить соседние корзины на наличие "почти дубликатов". Если есть, вернуть true.
Если текущая корзина пуста и в соседних корзинах нет "почти дубликатов", добавить текущий элемент в соответствующую корзину.
Если текущий индекс превышает k, удалить элемент из корзины, которая вышла за пределы окна.

3⃣Завершение:
Если ни одна пара "почти дубликатов" не найдена, вернуть false.

😎 Решение:
public class Solution {
public bool ContainsNearbyAlmostDuplicate(int[] nums, int k, int t) {
if (t < 0) return false;
Dictionary<long, long> buckets = new Dictionary<long, long>();
long w = (long)t + 1;

for (int i = 0; i < nums.Length; i++) {
long bucket = GetID(nums[i], w);
if (buckets.ContainsKey(bucket)) return true;
if (buckets.ContainsKey(bucket - 1) && Math.Abs(nums[i] - buckets[bucket - 1]) < w) return true;
if (buckets.ContainsKey(bucket + 1) && Math.Abs(nums[i] - buckets[bucket + 1]) < w) return true;
buckets[bucket] = nums[i];
if (i >= k) {
buckets.Remove(GetID(nums[i - k], w));
}
}
return false;
}

private long GetID(long x, long w) {
return x < 0 ? (x + 1) / w - 1 : x / w;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1232. Check If It Is a Straight Line
Сложность: easy

Вам дан массив координат, coordinates[i] = [x, y], где [x, y] - координаты точки. Проверьте, образуют ли эти точки прямую линию в плоскости XY.

Пример:
Input: coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
Output: true


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

1⃣Определение наклона:
Вычисляем наклон между первыми двумя точками и используем его как эталон.

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

3⃣Если все наклоны совпадают, то все точки лежат на одной прямой.

😎 Решение:
public class Solution {
public bool CheckStraightLine(int[][] coordinates) {
int x0 = coordinates[0][0], y0 = coordinates[0][1];
int x1 = coordinates[1][0], y1 = coordinates[1][1];

foreach (var point in coordinates) {
int x = point[0], y = point[1];
if ((x1 - x0) * (y - y0) != (y1 - y0) * (x - x0)) {
return false;
}
}
return true;
}
}


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

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

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


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

1⃣Установить границы бинарного поиска: влево = 0, вправо = n - 1.

2⃣Выполнить бинарный поиск: сравнить середину с целью и сменить границу.

3⃣Вернуть влево как позицию вставки, если цель не найдена

😎 Решение:
public class Solution {
public int SearchInsert(int[] nums, int target) {
int left = 0, right = nums.Length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
if (target < nums[mid]) right = mid - 1;
else left = mid + 1;
}
return left;
}
}


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

Вам дан массив из n строк одинаковой длины. Мы можем выбрать любые индексы удаления и удалить все символы в этих индексах для каждой строки.

Например, если у нас есть strs = ["abcdef", "uvwxyz"] и индексы удаления {0, 2, 3}, то конечный массив после удаления будет ["bef", "vyz"]. Предположим, что мы выбрали набор индексов удаления answer таким образом, что после удаления конечный массив имеет элементы в лексикографическом порядке (т.е, strs[0] <= strs[1] <= strs[2] <= ... <= strs[n - 1]). Возвращает минимально возможное значение answer.length.

Пример:
Input: strs = ["ca","bb","ac"]
Output: 1


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

1⃣Определить количество строк n и длину каждой строки m.
Создать массив delete_count длиной m, который будет отслеживать количество удаляемых столбцов.

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

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

😎 Решение:
public class Solution {
public int MinDeletionSize(string[] strs) {
int n = strs.Length;
int m = strs[0].Length;
bool[] deleteCount = new bool[m];

bool IsSorted() {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < m; j++) {
if (deleteCount[j]) continue;
if (strs[i][j] > strs[i + 1][j]) return false;
if (strs[i][j] < strs[i + 1][j]) break;
}
}
return true;
}

while (!IsSorted()) {
for (int j = 0; j < m; j++) {
if (deleteCount[j]) continue;
for (int i = 0; i < n - 1; i++) {
if (strs[i][j] > strs[i + 1][j]) {
deleteCount[j] = true;
break;
}
}
if (deleteCount[j]) break;
}
}

int count = 0;
foreach (bool del in deleteCount) {
if (del) count++;
}
return count;
}
}


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

Реализуйте кеш с политикой LFU (наименее часто используемый):
Каждый элемент имеет счетчик частоты использования.
При обработке удаляется элемент с минимальной степенью защиты , а если таких несколько — исключительно недавно использованный

Пример:
Input
["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, 3, null, -1, 3, 4]


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

1⃣insert(int key, int frequency, int value):
Вставить пару частота-значение в cache с заданным ключом.
Получить LinkedHashSet, соответствующий данной частоте (по умолчанию пустой Set), и вставить в него ключ.

2⃣int get(int key):
Если ключа нет в кеше, вернуть -1.
Получить частоту и значение из кеша.
Удалить ключ из LinkedHashSet, связанного с частотой.
Если minf == frequency и LinkedHashSet пуст, увеличить minf на 1 и удалить запись частоты из frequencies.
Вызвать insert(key, frequency + 1, value).
Вернуть значение.

3⃣void put(int key, int value):
Если capacity <= 0, выйти.
Если ключ существует, обновить значение и вызвать get(key).
Если размер кеша равен capacity, удалить первый элемент из LinkedHashSet, связанного с minf, и из кеша.
Установить minf в 1.
Вызвать insert(key, 1, value).

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

public class LFUCache {
private Dictionary<int, LinkedList<(int, int)>> frequencies = new Dictionary<int, LinkedList<(int, int)>>();
private Dictionary<int, (int, LinkedListNode<(int, int)>)> cache = new Dictionary<int, (int, LinkedListNode<(int, int)>)>();
private int capacity;
private int minf;

public LFUCache(int capacity) {
this.capacity = capacity;
this.minf = 0;
}
private void Insert(int key, int frequency, int value) {
if (!frequencies.ContainsKey(frequency)) {
frequencies[frequency] = new LinkedList<(int, int)>();
}
frequencies[frequency].AddLast((key, value));
cache[key] = (frequency, frequencies[frequency].Last);
}
public int Get(int key) {
if (!cache.ContainsKey(key)) return -1;
var (frequency, node) = cache[key];
frequencies[frequency].Remove(node);
if (frequencies[frequency].Count == 0) {
frequencies.Remove(frequency);
if (minf == frequency) minf++;
}
Insert(key, frequency + 1, node.Value.Item2);
return node.Value.Item2;
}
public void Put(int key, int value) {
if (capacity <= 0) return;
if (cache.ContainsKey(key)) {
cache[key].node.Value = (key, value);
Get(key);
return;
}
if (cache.Count == capacity) {
var oldest = frequencies[minf].First.Value;
frequencies[minf].RemoveFirst();
cache.Remove(oldest.Item1);
if (frequencies[minf].Count == 0) frequencies.Remove(minf);
}
minf = 1;
Insert(key, 1, value);
}
}


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

В этой задаче дерево — это неориентированный граф, который является связным и не содержит циклов.

Вам дан граф, который изначально был деревом с n узлами, пронумерованными от 1 до n, и к которому добавили одно дополнительное ребро. Добавленное ребро соединяет две разные вершины, выбранные из 1 до n, и это ребро не существовало ранее. Граф представлен массивом edges длины n, где edges[i] = [ai, bi] указывает на то, что существует ребро между узлами ai и bi в графе.

Верните ребро, которое можно удалить, чтобы результирующий граф стал деревом из n узлов. Если существует несколько ответов, верните тот, который встречается последним в исходных данных.
Пример:
Input: edges = [[1,2],[1,3],[2,3]]
Output: [2,3]


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

1⃣Для каждого ребра (u, v) создайте представление графа с использованием списка смежности. Это позволит легко выполнять обход в глубину (DFS) для проверки соединений между узлами.

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

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

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

public class Solution {
HashSet<int> seen = new HashSet<int>();
const int MAX_EDGE_VAL = 1000;

public int[] FindRedundantConnection(int[][] edges) {
List<int>[] graph = new List<int>[MAX_EDGE_VAL + 1];
for (int i = 0; i <= MAX_EDGE_VAL; i++) {
graph[i] = new List<int>();
}

foreach (var edge in edges) {
seen.Clear();
if (graph[edge[0]].Count > 0 && graph[edge[1]].Count > 0 &&
Dfs(graph, edge[0], edge[1])) {
return edge;
}
graph[edge[0]].Add(edge[1]);
graph[edge[1]].Add(edge[0]);
}
throw new InvalidOperationException("No redundant connection found");
}

public bool Dfs(List<int>[] graph, int source, int target) {
if (!seen.Contains(source)) {
seen.Add(source);
if (source == target) return true;
foreach (int nei in graph[source]) {
if (Dfs(graph, nei, target)) return true;
}
}
return false;
}
}


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

У вас есть n садов, помеченных от 1 до n, и массив paths, где paths[i] = [xi, yi] описывает двунаправленный путь между садом xi и садом yi. В каждом саду вы хотите посадить один из 4 типов цветов. Все сады имеют не более 3 путей, входящих и выходящих из него. Ваша задача - выбрать тип цветка для каждого сада так, чтобы для любых двух садов, соединенных путем, они имели разные типы цветов. Верните любой такой выбор в виде массива answer, где answer[i] - тип цветка, посаженного в (i+1)-м саду. Типы цветов обозначаются 1, 2, 3 или 4. Ответ гарантированно существует.

Пример:
Input: n = 3, paths = [[1,2],[2,3],[3,1]]
Output: [1,2,3]


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

1⃣Построение графа:
Создайте граф из садов и путей между ними.

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

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

😎 Решение:
public class Solution {
public int[] GardenNoAdj(int n, int[][] paths) {
List<int>[] graph = new List<int>[n];
for (int i = 0; i < n; i++) {
graph[i] = new List<int>();
}
foreach (var path in paths) {
graph[path[0] - 1].Add(path[1] - 1);
graph[path[1] - 1].Add(path[0] - 1);
}

int[] answer = new int[n];
for (int garden = 0; garden < n; garden++) {
bool[] used = new bool[5];
foreach (int neighbor in graph[garden]) {
used[answer[neighbor]] = true;
}
for (int flower = 1; flower <= 4; flower++) {
if (!used[flower]) {
answer[garden] = flower;
break;
}
}
}

return answer;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1506. Find Root of N-Ary Tree
Сложность: medium

Вам даны все узлы N-арного дерева в виде массива объектов Node, где каждый узел имеет уникальное значение.

Верните корень N-арного дерева.

Пример:
Input: tree = [1,null,3,2,4,null,5,6]
Output: [1,null,3,2,4,null,5,6]
Explanation: The tree from the input data is shown above.
The driver code creates the tree and gives findRoot the Node objects in an arbitrary order.
For example, the passed array could be [Node(5),Node(4),Node(3),Node(6),Node(2),Node(1)] or [Node(2),Node(6),Node(1),Node(3),Node(5),Node(4)].
The findRoot function should return the root Node(1), and the driver code will serialize it and compare with the input data.
The input data and serialized Node(1) are the same, so the test passes.


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

1⃣Используйте хэшсет (named as seen) для отслеживания всех посещенных дочерних узлов. В конечном итоге корневой узел не будет в этом множестве.

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

3⃣Посетите список еще раз. На этот раз у нас будут все дочерние узлы в хэшсете. Как только вы наткнетесь на узел, который не находится в хэшсете, это и будет корневой узел, который мы ищем.

😎 Решение:
public class Solution {
public Node FindRoot(IList<Node> tree) {
HashSet<int> seen = new HashSet<int>();

foreach (Node node in tree) {
foreach (Node child in node.children) {
seen.Add(child.val);
}
}

Node root = null;
foreach (Node node in tree) {
if (!seen.Contains(node.val)) {
root = node;
break;
}
}
return root;
}
}


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

Есть неориентированное связное дерево с n узлами, пронумерованными от 0 до n - 1, и n - 1 ребрами.

Вам даны целое число n и массив edges, где edges[i] = [ai, bi] указывает, что существует ребро между узлами ai и bi в дереве.

Верните массив answer длиной n, где answer[i] — это сумма расстояний между i-м узлом в дереве и всеми другими узлами.

Пример:
Input: n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Output: [8,12,6,10,10,10]
Explanation: The tree is shown above.
We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5)
equals 1 + 1 + 2 + 2 + 2 = 8.
Hence, answer[0] = 8, and so on.


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

1⃣Постройте дерево и выполните обход в глубину (DFS) для расчета количества узлов в поддереве и суммы расстояний до всех узлов поддерева.

2⃣На основе полученных данных рассчитайте сумму расстояний для корня дерева.

3⃣Выполните второй обход в глубину (DFS) для корректировки суммы расстояний для каждого узла на основе суммы расстояний его родительского узла.

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

public class Solution {
private int[] ans, count;
private List<HashSet<int>> graph;
private int N;

public int[] SumOfDistancesInTree(int N, int[][] edges) {
this.N = N;
graph = new List<HashSet<int>>(N);
ans = new int[N];
count = new int[N];

for (int i = 0; i < N; ++i) {
graph.Add(new HashSet<int>());
count[i] = 1;
}

foreach (var edge in edges) {
graph[edge[0]].Add(edge[1]);
graph[edge[1]].Add(edge[0]);
}

Dfs(0, -1);
Dfs2(0, -1);
return ans;
}

private void Dfs(int node, int parent) {
foreach (int child in graph[node]) {
if (child != parent) {
Dfs(child, node);
count[node] += count[child];
ans[node] += ans[child] + count[child];
}
}
}

private void Dfs2(int node, int parent) {
foreach (int child in graph[node]) {
if (child != parent) {
ans[child] = ans[node] - count[child] + N - count[child];
Dfs2(child, node);
}
}
}
}


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

Дано корневое дерево, уровень корня которого равен 1, уровень его детей равен 2 и так далее.

Верните наименьший уровень x, такой что сумма всех значений узлов на уровне x максимальна.

Пример:
Input: root = [1,7,0,7,-8,null,null]
Output: 2
Explanation:
Level 1 sum = 1.
Level 2 sum = 7 + 0 = 7.
Level 3 sum = 7 + -8 = -1.
So we return the level with the maximum sum which is level 2.


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

1⃣Создайте целочисленную переменную maxSum для отслеживания максимальной суммы значений узлов на любом уровне, начав с большого отрицательного значения. Создайте переменную ans для хранения ответа на задачу. Создайте переменную level для хранения текущего уровня, через который мы проходим, и инициализируйте её значением 0. Инициализируйте очередь q типа TreeNode и добавьте в неё корень.

2⃣Выполните обход в ширину (BFS) до тех пор, пока очередь не станет пустой: увеличьте level на 1 и инициализируйте sumAtCurrentLevel = 0 для вычисления суммы всех значений узлов на этом уровне. Проходите через все узлы на уровне, используя только q.size() количество узлов. Внутри этого внутреннего цикла извлекайте все узлы на текущем уровне один за другим, добавляя их значения к sumAtCurrentLevel и добавляя левых и правых детей (если они существуют) в очередь. Поймите, что после прохождения всех узлов на уровне в очереди остаются только узлы уровня +1.

3⃣После прохождения всех узлов на уровне проверьте, больше ли sumAtCurrentLevel, чем maxSum. Если maxSum < sumAtCurrentLevel, обновите переменную ответа на ans = level и установите maxSum = sumAtCurrentLevel. Верните ans.

😎 Решение:
public class Solution {
public int MaxLevelSum(TreeNode root) {
int maxSum = int.MinValue;
int ans = 0, level = 0;
Queue<TreeNode> q = new Queue<TreeNode>();

q.Enqueue(root);

while (q.Count > 0) {
level++;
int sumAtCurrentLevel = 0;
for (int sz = q.Count; sz > 0; --sz) {
TreeNode node = q.Dequeue();
sumAtCurrentLevel += node.val;
if (node.left != null) {
q.Enqueue(node.left);
}
if (node.right != null) {
q.Enqueue(node.right);
}
}

if (maxSum < sumAtCurrentLevel) {
maxSum = sumAtCurrentLevel;
ans = level;
}
}

return ans;
}
}


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

У вас есть два типа плиток: домино размером 2 x 1 и тромино. Вы можете вращать эти фигуры.

Дано целое число n. Верните количество способов выложить плитками доску размером 2 x n. Поскольку ответ может быть очень большим, верните его по модулю 10^9 + 7.

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

Пример:
Input: n = 3
Output: 5
Explanation: The five different ways are show above.


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

1⃣Начнем с f(n) и далее спустимся до базовых случаев, f(1), f(2) и p(2). Используйте те же определения для f и p из раздела Обзор. f(k): количество способов полностью покрыть доску шириной k. p(k): количество способов частично покрыть доску шириной k. Рекурсивные вызовы будут использовать результаты подзадач и базовых случаев, чтобы помочь нам получить окончательный результат, f(n).

2⃣Условие остановки для рекурсивных вызовов - когда k достигает базового случая (т.е. k <= 2). Значения для базовых случаев будут возвращены напрямую, вместо того чтобы делать дополнительные рекурсивные вызовы. f(1)=1, f(2)=2, p(2)=1. Чтобы избежать повторных вычислений, мы будем использовать 2 хэшмапы (f_cache и p_cache) для хранения рассчитанных значений для f и p. В Python встроенный декоратор @cache автоматически поддерживает эти хэшмапы для нас.

3⃣Если k больше 2, мы будем делать рекурсивные вызовы к f и p в соответствии с переходной функцией: f(k) = f(k−1) + f(k−2) + 2 * p(k−1), p(k) = p(k−1) + f(k−2). f(n) будет возвращено, как только все рекурсивные вызовы завершатся.

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

public class Solution {
private const int MOD = 1_000_000_007;
private Dictionary<int, int> fCache = new Dictionary<int, int>();
private Dictionary<int, int> pCache = new Dictionary<int, int>();

private int p(int n) {
if (pCache.ContainsKey(n)) {
return pCache[n];
}
if (n == 2) {
return 1;
}
int result = (p(n - 1) + f(n - 2)) % MOD;
pCache[n] = result;
return result;
}

private int f(int n) {
if (fCache.ContainsKey(n)) {
return fCache[n];
}
if (n <= 2) {
return n;
}
int result = (f(n - 1) + f(n - 2) + 2 * p(n - 1)) % MOD;
fCache[n] = result;
return result;
}

public int NumTilings(int n) {
return f(n);
}
}


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

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

Пример:
Input: arr1 = [1,2,3,4,5], arr2 = [1,2,5,7,9], arr3 = [1,3,4,5,8]
Output: [1,5]
Explanation: Only 1 and 5 appeared in the three arrays.


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

1⃣Инициализируйте counter как TreeMap для записи чисел, которые появляются в трех массивах, и количество их появлений.

2⃣Пройдитесь по массивам arr1, arr2 и arr3, чтобы посчитать частоты появления элементов.

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

😎 Решение:
public class Solution {
public IList<int> ArraysIntersection(int[] arr1, int[] arr2, int[] arr3) {
SortedDictionary<int, int> counter = new SortedDictionary<int, int>();

foreach (int e in arr1) counter[e] = counter.GetValueOrDefault(e, 0) + 1;
foreach (int e in arr2) counter[e] = counter.GetValueOrDefault(e, 0) + 1;
foreach (int e in arr3) counter[e] = counter.GetValueOrDefault(e, 0) + 1;

List<int> ans = new List<int>();
foreach (var entry in counter) {
if (entry.Value == 3) ans.Add(entry.Key);
}

return ans;
}
}


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

У вас есть несколько монет. Вероятность выпадения орла для i-й монеты равна prob[i].

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

Пример:
Input: prob = [0.5,0.5,0.5,0.5,0.5], target = 0
Output: 0.03125


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

1⃣Инициализация:
Создайте переменную n и инициализируйте её размером массива prob.
Создайте 2D массив dp размером n + 1 строк и target + 1 столбцов, где dp[i][j] хранит вероятность получить j орлов, используя первые i монет.
Установите базовый случай dp[0][0] = 1.

2⃣Итерация:
Используйте два вложенных цикла для заполнения массива dp.
Внешний цикл итерируется от i = 1 до n. Для каждого i установите dp[i][0], что обозначает вероятность получить 0 орлов при использовании i монет: dp[i][0] = dp[i - 1][0] * (1 - prob[i - 1]).
Внутренний цикл итерируется от j = 1 до target. Для каждого j вычислите dp[i][j] по формуле: dp[i][j] = dp[i - 1][j - 1] * prob[i - 1] + dp[i - 1][j] * (1 - prob[i - 1]).

3⃣Возврат результата:
Верните значение dp[n][target], которое содержит искомую вероятность.

😎 Решение:
public class Solution {
public double ProbabilityOfHeads(double[] prob, int target) {
int n = prob.Length;
double[][] dp = new double[n + 1][];
for (int i = 0; i <= n; i++) {
dp[i] = new double[target + 1];
}
dp[0][0] = 1.0;

for (int i = 1; i <= n; ++i) {
dp[i][0] = dp[i - 1][0] * (1 - prob[i - 1]);
for (int j = 1; j <= target; ++j) {
if (j <= i) {
dp[i][j] = dp[i - 1][j - 1] * prob[i - 1] + dp[i - 1][j] * (1 - prob[i - 1]);
}
}
}

return dp[n][target];
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1358. Number of Substrings Containing All Three Characters
Сложность: medium

Дана строка s, состоящая только из символов a, b и c.

Верните количество подстрок, содержащих хотя бы одно вхождение всех этих символов a, b и c.

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


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

1⃣Инициализация указателей и счетчиков:
Создайте три указателя i, j, и count для отслеживания текущего положения в строке и подсчета подстрок. Используйте словарь для подсчета вхождений символов a, b, и c.

2⃣Расширение окна:
Перемещайте правый указатель j по строке и увеличивайте счетчики символов в словаре. Как только все три символа (a, b, и c) присутствуют в текущем окне, начинайте уменьшать левый указатель i.

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

😎 Решение:
public class Solution {
public int NumberOfSubstrings(string s) {
int count = 0;
int[] charCount = new int[3];
int i = 0;

for (int j = 0; j < s.Length; j++) {
charCount[s[j] - 'a']++;

while (charCount[0] > 0 && charCount[1] > 0 && charCount[2] > 0) {
count += s.Length - j;
charCount[s[i] - 'a']--;
i++;
}
}

return count;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Задача: 1012. Numbers With Repeated Digits
Сложность: hard

Задав целое число n, верните количество положительных целых чисел в диапазоне [1, n], у которых хотя бы одна цифра повторяется.

Пример:
Input: n = 20
Output: 1


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

1⃣Вычисление всех чисел с уникальными цифрами:
Найдите количество чисел в диапазоне [1, n], у которых все цифры уникальны. Для этого используйте перебор всех чисел и проверку уникальности цифр.

2⃣Вычисление всех чисел в диапазоне [1, n]:
Это значение равно n, поскольку это количество всех чисел от 1 до n включительно.

3⃣Вычисление результата:
Вычтите количество чисел с уникальными цифрами из общего количества чисел, чтобы получить количество чисел с повторяющимися цифрами.

😎 Решение:
public class Solution {
public int NumDupDigitsAtMostN(int n) {
return n - CountUniqueDigitNumbers(n);
}

private int CountUniqueDigitNumbers(int x) {
var s = x.ToString();
var n = s.Length;
var res = 0;

for (var i = 1; i < n; i++) {
res += 9 * Permutation(9, i - 1);
}

var used = new HashSet<int>();
for (var i = 0; i < n; i++) {
for (var j = (i == 0 ? 1 : 0); j < s[i] - '0'; j++) {
if (!used.Contains(j)) {
res += Permutation(9 - i, n - i - 1);
}
}
if (used.Contains(s[i] - '0')) {
break;
}
used.Add(s[i] - '0');
}

if (used.Count == n) {
res++;
}

return res;
}

private int Permutation(int m, int n) {
return n == 0 ? 1 : Permutation(m, n - 1) * (m - n + 1);
}
}


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