C# | LeetCode
3.48K subscribers
162 photos
1 video
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
Задача: 976. Largest Perimeter Triangle
Сложность: easy

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

Пример:
Input: nums = [1,2,1,10]
Output: 0
Explanation:
You cannot use the side lengths 1, 1, and 2 to form a triangle.
You cannot use the side lengths 1, 1, and 10 to form a triangle.
You cannot use the side lengths 1, 2, and 10 to form a triangle.
As we cannot use any three side lengths to form a triangle of non-zero area, we return 0.


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

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

2⃣Для каждого элемента c в массиве, начиная с конца: Выберите два наибольших возможных значения a и b, которые находятся перед c в отсортированном массиве (т.е. значения, смежные с c). Проверьте, образуют ли a, b и c треугольник (условие треугольника: a + b > c). Если образуют, верните их сумму как периметр треугольника.

3⃣Если не удалось найти такие значения, верните 0.

😎 Решение:
public class Solution {
public int LargestPerimeter(int[] A) {
Array.Sort(A);
for (int i = A.Length - 3; i >= 0; --i)
if (A[i] + A[i + 1] > A[i + 2])
return A[i] + A[i + 1] + A[i + 2];
return 0;
}
}


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

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

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


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

1⃣Заполнение стека по стратегии право->узел->лево:
Инициируйте стек и добавьте в него корень дерева.
Перед тем как положить узел в стек, сначала добавьте его правого потомка, затем сам узел, а после — левого потомка. Это обеспечит последовательное извлечение узлов из стека в нужном порядке для постпорядкового обхода.

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

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

😎 Решение:
public class Solution {
public IList<int> PostorderTraversal(TreeNode root) {
List<int> output = new List<int>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if (root == null)
return output;
stack.Push(root);
while (stack.Count > 0) {
root = stack.Pop();
output.Add(root.val);
if (root.left != null)
stack.Push(root.left);
if (root.right != null)
stack.Push(root.right);
}

output.Reverse();
return output;
}
}


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

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

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

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


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

1⃣Инициализируйте указатель lastNonZeroFoundAt, чтобы зафиксировать позицию последнего ненулевого элемента.

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

3⃣Продолжайте до конца массива — все не будут впереди, они сместятся в конце.

😎 Решение:
public class Solution {
public void MoveZeroes(int[] nums) {
int lastNonZeroFoundAt = 0;
for (int cur = 0; cur < nums.Length; cur++) {
if (nums[cur] != 0) {
int temp = nums[lastNonZeroFoundAt];
nums[lastNonZeroFoundAt] = nums[cur];
nums[cur] = temp;
lastNonZeroFoundAt++;
}
}
}
}


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

Дерево — это неориентированный граф, в котором любые две вершины соединены ровно одним путем. Другими словами, любое связное граф без простых циклов является деревом.

Дано дерево из n узлов, помеченных от 0 до n - 1, и массив из n - 1 ребер, где edges[i] = [ai, bi] указывает на наличие неориентированного ребра между узлами ai и bi в дереве. Вы можете выбрать любой узел дерева в качестве корня. Когда вы выбираете узел x в качестве корня, дерево имеет высоту h. Среди всех возможных корневых деревьев те, которые имеют минимальную высоту (то есть min(h)), называются деревьями с минимальной высотой (MHT).

Верните список всех меток корней MHT. Вы можете вернуть ответ в любом порядке.

Высота корневого дерева — это количество ребер на самом длинном нисходящем пути между корнем и листом.

Пример:
Input: n = 4, edges = [[1,0],[1,2],[1,3]]
Output: [1]
Explanation: As shown, the height of the tree is 1 when the root is the node with label 1 which is the only MHT.


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

1⃣Создание списка смежности
Создайте список смежности, представляющий граф.

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

3⃣Повторение процесса
Повторяйте процесс до тех пор, пока не останется два или менее узлов. Эти узлы будут корнями деревьев с минимальной высотой (MHT).

😎 Решение:
public class Solution {
public IList<int> FindMinHeightTrees(int n, int[][] edges) {
if (n == 1) return new List<int> { 0 };

var adj = new List<HashSet<int>>();
for (int i = 0; i < n; i++) adj.Add(new HashSet<int>());
foreach (var edge in edges) {
adj[edge[0]].Add(edge[1]);
adj[edge[1]].Add(edge[0]);
}

var leaves = new List<int>();
for (int i = 0; i < n; i++) {
if (adj[i].Count == 1) leaves.Add(i);
}

int remainingNodes = n;
while (remainingNodes > 2) {
remainingNodes -= leaves.Count;
var newLeaves = new List<int>();
foreach (var leaf in leaves) {
var neighbor = adj[leaf].First();
adj[neighbor].Remove(leaf);
if (adj[neighbor].Count == 1) newLeaves.Add(neighbor);
}
leaves = newLeaves;
}

return leaves;
}
}


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

Вам дано целое число n, количество узлов в ориентированном графе, где узлы помечены от 0 до n - 1. Каждое ребро в этом графе может быть красным или синим, и могут быть самопетли и параллельные ребра.

Вам даны два массива redEdges и blueEdges, где:
redEdges[i] = [ai, bi] указывает, что в графе существует направленное красное ребро от узла ai к узлу bi, и
blueEdges[j] = [uj, vj] указывает, что в графе существует направленное синее ребро от узла uj к узлу vj.
Верните массив answer длины n, где каждый answer[x] — это длина кратчайшего пути от узла 0 до узла x, такого что цвета ребер чередуются вдоль пути, или -1, если такого пути не существует.

Пример:
Input: n = 3, redEdges = [[0,1],[1,2]], blueEdges = []
Output: [0,1,-1]


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

1⃣Создание структуры данных и инициализация:
Создайте список смежности adj, который будет содержать пары (сосед, цвет) для каждого узла.
Создайте массив answer длиной n, инициализированный значением -1, чтобы хранить длину кратчайшего пути для каждого узла.
Создайте 2D массив visit для отслеживания, были ли узлы посещены с использованием ребра определённого цвета.

2⃣Инициализация очереди и начальных условий:
Создайте очередь для хранения трёх значений (узел, количество шагов, цвет предыдущего ребра).
Добавьте в очередь начальный узел (0, 0, -1) и установите visit[0][0] и visit[0][1] в true, так как повторное посещение узла 0 бессмысленно.

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

😎 Решение:
public class Solution {
public int[] ShortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) {
var adj = new Dictionary<int, List<(int, int)>>();
foreach (var edge in redEdges) {
if (!adj.ContainsKey(edge[0])) adj[edge[0]] = new List<(int, int)>();
adj[edge[0]].Add((edge[1], 0));
}
foreach (var edge in blueEdges) {
if (!adj.ContainsKey(edge[0])) adj[edge[0]] = new List<(int, int)>();
adj[edge[0]].Add((edge[1], 1));
}

var answer = new int[n];
Array.Fill(answer, -1);
var visit = new bool[n, 2];
var queue = new Queue<(int node, int steps, int prevColor)>();
queue.Enqueue((0, 0, -1));
answer[0] = 0;
visit[0, 0] = true;
visit[0, 1] = true;

while (queue.Count > 0) {
var (node, steps, prevColor) = queue.Dequeue();
if (!adj.ContainsKey(node)) continue;

foreach (var (neighbor, color) in adj[node]) {
if (!visit[neighbor, color] && color != prevColor) {
if (answer[neighbor] == -1) answer[neighbor] = steps + 1;
visit[neighbor, color] = true;
queue.Enqueue((neighbor, steps + 1, color));
}
}
}
return answer;
}
}


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

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

Подпоследовательность — это последовательность, которую можно получить из arr, удалив некоторые или ни одного элемента, не меняя порядок оставшихся элементов.

Пример:
Input: arr = [1,5,7,8,5,3,4,2,1], difference = -2
Output: 4
Explanation: The longest arithmetic subsequence is [7,5,3,1].


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

1⃣Инициализируйте пустой хеш-таблицу dp и установите answer = 1.

2⃣Итеративно обработайте массив arr. Для каждого элемента arr[i]:
Вычислите before_a, максимальную длину арифметической подпоследовательности, заканчивающейся на arr[i] - difference:
- если arr[i] - difference существует в dp, установите before_a = dp[arr[i] - difference].
- в противном случае, установите before_a = 0.
Установите dp[arr[i]] = before_a + 1, обновите answer как answer = max(answer, dp[arr[i]]).

3⃣Верните answer после завершения итерации.

😎 Решение:
public class Solution {
public int LongestSubsequence(int[] arr, int difference) {
Dictionary<int, int> dp = new Dictionary<int, int>();
int answer = 1;

foreach (int a in arr) {
int beforeA = dp.ContainsKey(a - difference) ? dp[a - difference] : 0;
dp[a] = beforeA + 1;
answer = Math.Max(answer, dp[a]);
}

return answer;
}
}


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

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

Реализуйте класс FirstUnique:
- FirstUnique(int[] nums) Инициализирует объект числами в очереди.
- int showFirstUnique() возвращает значение первого уникального элемента в очереди и возвращает -1, если такого элемента нет.
- void add(int value) вставляет значение в очередь.

Пример:
Input: 
["FirstUnique","showFirstUnique","add","showFirstUnique","add","showFirstUnique","add","showFirstUnique"]
[[[2,3,5]],[],[5],[],[2],[],[3],[]]
Output:
[null,2,null,2,null,3,null,-1]
Explanation:
FirstUnique firstUnique = new FirstUnique([2,3,5]);
firstUnique.showFirstUnique(); // return 2
firstUnique.add(5); // the queue is now [2,3,5,5]
firstUnique.showFirstUnique(); // return 2
firstUnique.add(2); // the queue is now [2,3,5,5,2]
firstUnique.showFirstUnique(); // return 3
firstUnique.add(3); // the queue is now [2,3,5,5,2,3]
firstUnique.showFirstUnique(); // return -1


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

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

2⃣Метод showFirstUnique возвращает значение первого уникального элемента в очереди, если таковой существует, или -1, если уникальных элементов нет.

3⃣Метод add добавляет новое значение в очередь. Если значение уже было добавлено ранее, обновляет его статус уникальности и удаляет его из множества уникальных значений, если оно больше не уникально.

😎 Решение:
public class FirstUnique {
private LinkedList<int> queue;
private Dictionary<int, LinkedListNode<int>> map;
private HashSet<int> duplicates;

public FirstUnique(int[] nums) {
queue = new LinkedList<int>();
map = new Dictionary<int, LinkedListNode<int>>();
duplicates = new HashSet<int>();
foreach (var num in nums) {
Add(num);
}
}

public int ShowFirstUnique() {
if (queue.Count > 0) {
return queue.First.Value;
}
return -1;
}

public void Add(int value) {
if (duplicates.Contains(value)) {
return;
}

if (map.ContainsKey(value)) {
queue.Remove(map[value]);
map.Remove(value);
duplicates.Add(value);
} else {
var node = new LinkedListNode<int>(value);
queue.AddLast(node);
map[value] = node;
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Задача: CodeTestcaseTest ResultTest Result1187. Make Array Strictly Increasing
Сложность: hard

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

В одной операции вы можете выбрать два индекса 0 <= i < arr1.length и 0 <= j < arr2.length и выполнить присваивание arr1[i] = arr2[j].

Если нет способа сделать arr1 строго возрастающим, верните -1.

Пример:
Input: arr1 = [1,5,3,6,7], arr2 = [1,3,2,4]
Output: 1
Explanation: Replace 5 with 2, then arr1 = [1, 2, 3, 6, 7].


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

1⃣ Сначала отсортируйте массив arr2 и инициализируйте хэш-таблицу dp для хранения промежуточных результатов. Определите функцию dfs(i, prev), которая вычисляет минимальное количество операций для сортировки массива arr1, начиная с индекса i, при условии, что предыдущий элемент равен prev. Если результат для (i, prev) уже существует в dp, то просто верните сохраненное значение.

2⃣Внутри функции dfs инициализируйте переменную cost значением float('inf'). Если arr1[i] больше, чем prev, обновите значение cost результатом вызова dfs(i + 1, arr1[i]). Используйте бинарный поиск, чтобы найти индекс idx наименьшего значения в arr2, которое больше prev. Если такой индекс существует, обновите значение cost результатом минимального значения между текущим значением cost и 1 + dfs(i + 1, arr2[idx]).

3⃣После всех вычислений обновите dp[(i, prev)] значением cost и верните cost. В конце вызовите dfs(0, -1) и верните его значение, если оно не равно float('inf'); в противном случае верните -1.

😎 Решение:
public class Solution {
public int MakeArrayIncreasing(int[] arr1, int[] arr2) {
Array.Sort(arr2);
int answer = Dfs(0, -1, arr1, arr2);
return answer < 2001 ? answer : -1;
}

Dictionary<(int, int), int> dp = new Dictionary<(int, int), int>();

private int Dfs(int i, int prev, int[] arr1, int[] arr2) {
if (i == arr1.Length) {
return 0;
}
var key = (i, prev);
if (dp.ContainsKey(key)) {
return dp[key];
}

int cost = 2001;
if (arr1[i] > prev) {
cost = Dfs(i + 1, arr1[i], arr1, arr2);
}

int idx = Array.BinarySearch(arr2, prev + 1);
if (idx < 0) {
idx = ~idx;
}
if (idx < arr2.Length) {
cost = Math.Min(cost, 1 + Dfs(i + 1, arr2[idx], arr1, arr2));
}

dp[key] = cost;
return cost;
}
}


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

Вы запланировали несколько поездок на поезде за год вперёд. Дни года, в которые вы будете путешествовать, заданы в виде целочисленного массива days. Каждый день — это целое число от 1 до 365.

Билеты на поезд продаются тремя различными способами:

однодневный билет продаётся за costs[0] долларов,
семидневный билет продаётся за costs[1] долларов, и
тридцатидневный билет продаётся за costs[2] долларов.
Билеты позволяют путешествовать указанное количество дней подряд.

Например, если мы покупаем семидневный билет на 2-й день, то мы можем путешествовать в течение 7 дней: 2, 3, 4, 5, 6, 7 и 8.
Верните минимальное количество долларов, которое вам нужно, чтобы путешествовать каждый день, указанный в списке days.

Пример:
Input: days = [1,4,6,7,8,20], costs = [2,7,15]
Output: 11
Explanation: For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1.
On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9.
On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20.
In total, you spent $11 and covered all the days of your travel.


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

1⃣Создайте массив dp размером на один больше последнего дня, в который нужно путешествовать. Инициализируйте все значения -1, что означает, что ответ для этого дня ещё не был вычислен. Также создайте хэш-набор isTravelNeeded из days.

2⃣Создайте функцию solve, которая принимает аргумент currDay. Если currDay больше последнего дня, когда нужно путешествовать, просто верните 0, так как все дни уже покрыты. Если currDay отсутствует в isTravelNeeded, перейдите к currDay + 1. Если ответ для currDay в массиве dp не равен -1, это означает, что ответ уже был вычислен, поэтому просто верните его.

3⃣Найдите стоимость трёх билетов, которые можно использовать в этот день, добавьте соответствующую стоимость и обновите dp[currDay] соответственно в рекурсивном вызове. Вызовите solve, передав currDay = 1, и верните ответ.

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

public class Solution {
private HashSet<int> isTravelNeeded = new HashSet<int>();

private int Solve(int[] dp, int[] days, int[] costs, int currDay) {
if (currDay > days[days.Length - 1]) {
return 0;
}

if (!isTravelNeeded.Contains(currDay)) {
return Solve(dp, days, costs, currDay + 1);
}

if (dp[currDay] != -1) {
return dp[currDay];
}

int oneDay = costs[0] + Solve(dp, days, costs, currDay + 1);
int sevenDay = costs[1] + Solve(dp, days, costs, currDay + 7);
int thirtyDay = costs[2] + Solve(dp, days, costs, currDay + 30);

dp[currDay] = Math.Min(oneDay, Math.Min(sevenDay, thirtyDay));
return dp[currDay];
}

public int MincostTickets(int[] days, int[] costs) {
int lastDay = days[days.Length - 1];
int[] dp = new int[lastDay + 1];
for (int i = 0; i <= lastDay; i++) {
dp[i] = -1;
}

foreach (int day in days) {
isTravelNeeded.Add(day);
}

return Solve(dp, days, costs, 1);
}
}


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

Вам дана бинарная матрица grid размером m x n.

Ход состоит из выбора любой строки или столбца и переключения каждого значения в этой строке или столбце (т.е. изменение всех 0 на 1, и всех 1 на 0).

Каждая строка матрицы интерпретируется как двоичное число, и счёт матрицы — это сумма этих чисел.

Верните наивысший возможный счёт после выполнения любого количества ходов (включая ноль ходов).

Пример:
Input: grid = [[0,0,1,1],[1,0,1,0],[1,1,0,0]]
Output: 39
Explanation: 0b1111 + 0b1001 + 0b1111 = 15 + 9 + 15 = 39


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

1⃣Инициализируйте переменные: m и n для количества строк и столбцов в grid, score для хранения максимального счёта матрицы. Пройдитесь по первому столбцу матрицы. Если элемент равен 0, переверните всю строку.

2⃣Пройдитесь по матрице от второго до последнего столбца. Для каждого столбца посчитайте количество нулей (countZero). Если количество нулей больше, переверните весь столбец.

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

😎 Решение:
public class Solution {
public int MatrixScore(int[][] grid) {
int m = grid.Length, n = grid[0].Length;

for (int i = 0; i < m; i++) {
if (grid[i][0] == 0) {
for (int j = 0; j < n; j++) {
grid[i][j] ^= 1;
}
}
}

for (int j = 1; j < n; j++) {
int countZero = 0;
for (int i = 0; i < m; i++) {
if (grid[i][j] == 0) {
countZero++;
}
}
if (countZero > m / 2) {
for (int i = 0; i < m; i++) {
grid[i][j] ^= 1;
}
}
}

int score = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
score += 1 << (n - j - 1);
}
}
}

return score;
}
}


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

Перед вами замок с 4 круглыми колесами. Каждое колесо имеет 10 слотов: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. Колеса могут свободно вращаться и оборачиваться: например, мы можем повернуть "9" так, чтобы получился "0", или "0" так, чтобы получился "9". Каждый ход состоит из поворота одного колеса на один слот. Изначально замок начинается с '0000', строки, представляющей состояние 4 колес. Вам дан список тупиков, то есть если замок отобразит любой из этих кодов, колеса замка перестанут вращаться, и вы не сможете его открыть. Учитывая цель, представляющую значение колес, которое позволит отпереть замок, верните минимальное общее количество оборотов, необходимое для открытия замка, или -1, если это невозможно.

Пример:
Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
Output: 6


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

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

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

3⃣Если очередь пуста и целевое состояние не найдено, верните -1.

😎 Решение:
public class Solution {
public int OpenLock(string[] deadends, string target) {
var dead = new HashSet<string>(deadends);
var queue = new Queue<(string, int)>();
queue.Enqueue(("0000", 0));
var visited = new HashSet<string> { "0000" };

while (queue.Count > 0) {
var (node, steps) = queue.Dequeue();
if (node == target) return steps;
if (dead.Contains(node)) continue;
foreach (var neighbor in Neighbors(node)) {
if (!visited.Contains(neighbor)) {
visited.Add(neighbor);
queue.Enqueue((neighbor, steps + 1));
}
}
}

return -1;
}

private IEnumerable<string> Neighbors(string node) {
var res = new List<string>();
var nodeArray = node.ToCharArray();
for (int i = 0; i < 4; i++) {
var x = nodeArray[i] - '0';
for (int d = -1; d <= 1; d += 2) {
var y = (x + d + 10) % 10;
nodeArray[i] = (char)(y + '0');
res.Add(new string(nodeArray));
nodeArray[i] = (char)(x + '0');
}
}
return res;
}
}


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

Двоичная строка является монотонно возрастающей, если она состоит из некоторого количества 0 (возможно, ни одного), за которым следует некоторое количество 1 (также возможно, ни одного). Вам дана двоичная строка s. Вы можете перевернуть s[i], изменив ее значение с 0 на 1 или с 1 на 0.

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


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

1⃣Создать массив left для подсчета количества операций, чтобы сделать подстроку до текущего индекса монотонной (только 0).

2⃣Создать массив right для подсчета количества операций, чтобы сделать подстроку после текущего индекса монотонной (только 1).
Пройти по строке и заполнить массивы left и right.

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

😎 Решение:
public class Solution {
public int MinFlipsMonoIncr(string s) {
int n = s.Length;
int[] left = new int[n + 1];
int[] right = new int[n + 1];

for (int i = 0; i < n; i++) {
left[i + 1] = left[i] + (s[i] == '1' ? 1 : 0);
}

for (int i = n - 1; i >= 0; i--) {
right[i] = right[i + 1] + (s[i] == '0' ? 1 : 0);
}

int result = int.MaxValue;
for (int i = 0; i <= n; i++) {
result = Math.Min(result, left[i] + right[i]);
}
return result;
}
}


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

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

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


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

1⃣Создайте хеш-карту memo, где memo[(start, end)] содержит список корневых узлов всех возможных BST (деревьев бинарного поиска) с диапазоном значений узлов от start до end. Реализуем рекурсивную функцию allPossibleBST, которая принимает начальный диапазон значений узлов start, конечный диапазон end и memo в качестве параметров. Она возвращает список TreeNode, соответствующих всем BST, которые могут быть сформированы с этим диапазоном значений узлов. Вызываем allPossibleBST(1, n, memo) и выполняем следующее:

2⃣Объявляем список TreeNode под названием res для хранения списка корневых узлов всех возможных BST. Если start > end, мы добавляем null в res и возвращаем его. Если мы уже решили эту подзадачу, т.е. memo содержит пару (start, end), мы возвращаем memo[(start, end)].

3⃣Выбираем значение корневого узла от i = start до end, увеличивая i на 1 после каждой итерации. Рекурсивно вызываем leftSubtrees = allPossibleBST(start, i - 1, memo) и rightSubTrees = allPossibleBST(i + 1, end, memo). Перебираем все пары между leftSubtrees и rightSubTrees и создаем новый корень со значением i для каждой пары. Добавляем корень новообразованного BST в res. Устанавливаем memo[(start, end)] = res и возвращаем res.

😎 Решение:
public class Solution {
public IList<TreeNode> AllPossibleBST(
int start, int end, Dictionary<(int, int), IList<TreeNode>> memo) {
List<TreeNode> res = new List<TreeNode>();
if (start > end) {
res.Add(null);
return res;
}

var key = (start, end);
if (memo.ContainsKey(key)) {
return memo[key];
}

for (int i = start; i <= end; ++i) {
IList<TreeNode> leftSubTrees = AllPossibleBST(start, i - 1, memo);
IList<TreeNode> rightSubTrees = AllPossibleBST(i + 1, end, memo);
foreach (TreeNode left in leftSubTrees) {
foreach (TreeNode right in rightSubTrees) {
TreeNode root = new TreeNode(i, left, right);
res.Add(root);
}
}
}

memo[key] = res;
return res;
}

public IList<TreeNode> GenerateTrees(int n) {
var memo = new Dictionary<(int, int), IList<TreeNode>>();
return AllPossibleBST(1, n, memo);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
📺 База 1000+ реальных собеседований

На программиста, тестировщика, аналитика, проджекта и другие IT профы.

Есть собесы от ведущих компаний: Сбер, Яндекс, ВТБ, Тинькофф, Озон, Wildberries и т.д.

🎯 Переходи по ссылке и присоединяйся к базе, чтобы прокачать свои шансы на успешное трудоустройство!
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram опубликовал список 8 самых быстрорастущих каналов для программистов:

Only Python — Подборки приёмов и фич, о которых не рассказывают в курсах.

Only Tech — Главные тренды и инсайды из мира технологий, маркетинга и интернет-культуры.

Only Hack — Реальные кейсы кибератак, инструменты и методы защиты, которые используют хакеры.

Only GitHub — Репозитории, которые решают реальные задачи.
Скрипты, фреймворки и готовые решения

Only IT — Без мнений и слухов — только факты и важные IT-события.

Only Apple — Новые апдейты, утечки и фишки, которые Apple ещё не показала.

Only GPT — Промпты, хаки и свежие инструменты, о которых молчат даже AI-каналы.

Only Memes — Если ты когда-нибудь деплоил в пятницу вечером — ты поймешь

Подписывайтесь и прокачивайте свои скиллы.
Задача: 463. Island Perimeter
Сложность: easy

Дан массив размером row x col, представляющий карту, где grid[i][j] = 1 обозначает сушу, а grid[i][j] = 0 обозначает воду.

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

У острова нет "озёр", то есть вода внутри не соединена с водой вокруг острова. Одна ячейка - это квадрат со стороной 1. Сетка прямоугольная, ширина и высота не превышают 100. Определите периметр острова.

Пример:
Input: grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
Output: 16
Explanation: The perimeter is the 16 yellow stripes in the image above.


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

1⃣Пройти через каждую ячейку сетки и, когда вы находитесь в ячейке с значением 1 (ячейка суши), проверить окружающие (СВЕРХУ, СПРАВА, СНИЗУ, СЛЕВА) ячейки.

2⃣Ячейка суши без каких-либо окружающих ячеек суши будет иметь периметр 4. Вычесть 1 за каждую окружающую ячейку суши.

3⃣Когда вы находитесь в ячейке с значением 0 (ячейка воды), ничего не делать. Просто перейти к следующей ячейке.

😎 Решение:
public class Solution {
public int IslandPerimeter(int[][] grid) {
int rows = grid.Length;
int cols = grid[0].Length;

int result = 0;

for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] == 1) {
int up = (r == 0) ? 0 : grid[r-1][c];
int left = (c == 0) ? 0 : grid[r][c-1];
int down = (r == rows-1) ? 0 : grid[r+1][c];
int right = (c == cols-1) ? 0 : grid[r][c+1];

result += 4 - (up + left + right + down);
}
}
}

return result;
}
}


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