uz
Feedback
C# | LeetCode

C# | LeetCode

Kanalga Telegram’da o‘tish
3 286
Obunachilar
-424 soatlar
-77 kunlar
-1930 kunlar
Postlar arxiv
#hard Задача: 124. Binary Tree Maximum Path Sum Вам дан массив цен, где prices[i] — это цена данной акции в i-й день. Найдите максимальную прибыль, которую вы можете получить. Вы можете совершить не более двух транзакций. Обратите внимание: вы не можете участвовать в нескольких транзакциях одновременно (то есть, вы должны продать акцию, прежде чем снова её купить). Пример:
Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1
👨‍💻 Алгоритм: 1️⃣Наивная реализация этой идеи заключалась бы в разделении последовательностей на две части и последующем перечислении каждой из подпоследовательностей, хотя это определенно не самое оптимизированное решение. Для последовательности длиной N у нас было бы N возможных разделений (включая отсутствие разделения), каждый элемент был бы посещен один раз в каждом разделении. В результате общая временная сложность этой наивной реализации составила бы O(N²). 2️⃣Мы могли бы сделать лучше, чем наивная реализация O(N²). Что касается алгоритмов разделяй и властвуй, одна из общих техник, которую мы можем применить для оптимизации временной сложности, называется динамическим программированием (DP), где мы меняем менее повторяющиеся вычисления на некоторое дополнительное пространство. В алгоритмах динамического программирования обычно мы создаем массив одного или двух измерений для сохранения промежуточных оптимальных результатов. В данной задаче мы бы использовали два массива, один массив сохранял бы результаты последовательности слева направо, а другой массив сохранял бы результаты последовательности справа налево. Для удобства мы могли бы назвать это двунаправленным динамическим программированием. 3️⃣Сначала мы обозначаем последовательность цен как Prices[i], с индексом начиная от 0 до N-1. Затем мы определяем два массива, а именно left_profits[i] и right_profits[i]. Как следует из названия, каждый элемент в массиве left_profits[i] будет содержать максимальную прибыль, которую можно получить от выполнения одной транзакции в левой подпоследовательности цен от индекса ноль до i, (т.е. Prices[0], Prices[1], ... Prices[i]). Например, для подпоследовательности [7, 1, 5] соответствующий left_profits[2] будет равен 4, что означает покупку по цене 1 и продажу по цене 5. И каждый элемент в массиве right_profits[i] будет содержать максимальную прибыль, которую можно получить от выполнения одной транзакции в правой подпоследовательности цен от индекса i до N-1, (т.е. Prices[i], Prices[i+1], ... Prices[N-1]). Например, для правой подпоследовательности [3, 6, 4] соответствующий right_profits[3] будет равен 3, что означает покупку по цене 3 и продажу по цене 6. Теперь, если мы разделим последовательность цен вокруг элемента с индексом i на две подпоследовательности, с левыми подпоследовательностями как Prices[0], Prices[1], ... Prices[i] и правой подпоследовательностью как Prices[i+1], ... Prices[N-1], то общая максимальная прибыль, которую мы получим от этого разделения (обозначенная как max_profits[i]), может быть выражена следующим образом: max_profits[i] = left_profits[i] + right_profits[i+1]

🔥 Битый код - канал для настоящих кодеров! 🔴 Тебе надоело сталкиваться с багами и ошибками в коде? 🔴 Хочешь прокачать свои
🔥 Битый код - канал для настоящих кодеров! 🔴 Тебе надоело сталкиваться с багами и ошибками в коде? 🔴 Хочешь прокачать свои навыки и узнать, как эффективно решать сложные задачи? ⭐️ Тогда тебе к нам! На канале Битый код ты найдешь: 🟡 Советы по оптимизации кода 🟡 Практические примеры и решения 🧠 Развивай свои навыки программирования вместе с нами и становись настоящим профессионалом! 🔥 Присоединяйся к Битому коду и учись исправлять ошибки как настоящий мастер.

🤯 Чтобы не сидеть в творческом беспорядке, структурируй его с помощью Куб прогресса. Тут ты найдешь кучу советов для ITшника
🤯 Чтобы не сидеть в творческом беспорядке, структурируй его с помощью Куб прогресса. Тут ты найдешь кучу советов для ITшника: 🟡 пользуйся полезными сайтами 🟡 подчеркивай интересные мысли 🛞 Начинай внедрять лайфхаки - создай структуру внутри себя

Repost from Backend
В приватной базе собесов уже больше 100 записей. Яндекс, Тиньков, Сбербанк, Самокат, Озон и другие крупные компании в базе.

#medium Задача: 117. Populating Next Right Pointers in Each Node II Вам дано бинарное дерево:
struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}
Заполните каждый указатель next, чтобы он указывал на следующий правый узел. Если следующего правого узла нет, указатель next должен быть установлен в NULL. Изначально все указатели next установлены в NULL. Пример:
Input: root = [1,2,3,4,5,null,7]
Output: [1,#,2,3,#,4,5,7,#]
Explanation: Given the above binary tree (Figure A), your function should populate each next pointer to point to its next right node, just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level.
👨‍💻 Алгоритм: 1️⃣Инициализируйте очередь Q, которая будет использоваться во время обхода. Существует несколько способов реализации обхода в ширину, особенно когда дело доходит до определения уровня конкретного узла. Можно добавить в очередь пару (узел, уровень), и каждый раз, когда мы добавляем детей узла, мы добавляем (node.left, parent_level+1) и (node.right, parent_level+1). Этот подход может оказаться неэффективным для нашего алгоритма, так как нам нужны все узлы на одном уровне, и потребуется дополнительная структура данных. 2️⃣Более эффективный с точки зрения памяти способ разделения узлов одного уровня - использовать разграничение между уровнями. Обычно мы вставляем в очередь элемент NULL, который обозначает конец предыдущего уровня и начало следующего. Это отличный подход, но он все равно потребует некоторого объема памяти, пропорционального количеству уровней в дереве. 3️⃣Подход, который мы будем использовать, предполагает структуру вложенных циклов, чтобы обойти необходимость NULL указателя. По сути, на каждом шаге мы фиксируем размер очереди, который всегда соответствует всем узлам на определенном уровне. Как только мы узнаем этот размер, мы обрабатываем только это количество элементов и больше ничего. К тому времени, как мы закончим обработку заданного количества элементов, в очереди окажутся все узлы следующего уровня. Вот псевдокод для этого:
while (!Q.empty())
{
    size = Q.size()
    for i in range 0..size
    {
        node = Q.pop()
        Q.push(node.left)
        Q.push(node.right)
    }
}
Мы начинаем с добавления корня дерева в очередь. Так как на уровне 0 есть только один узел, нам не нужно устанавливать какие-либо соединения, и мы можем перейти к циклу while. 😎 Решение:
public class Solution {
    public Node Connect(Node root) {
        if (root == null) {
            return root;
        }

        Queue<Node> Q = new Queue<Node>();
        Q.Enqueue(root);
        while (Q.Count > 0) {
            int size = Q.Count;
            for (int i = 0; i < size; i++) {
                Node node = Q.Dequeue();
                if (i < size - 1) {
                    node.next = Q.Peek();
                }
                if (node.left != null) {
                    Q.Enqueue(node.left);
                }
                if (node.right != null) {
                    Q.Enqueue(node.right);
                }
            }
        }
        return root;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

#medium Задача: 116. Populating Next Right Pointers in Each Node Вам дано идеальное бинарное дерево, где все листья находятся на одном уровне, и у каждого родителя есть два детей. Бинарное дерево имеет следующее определение:
struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}
Заполните каждый указатель next так, чтобы он указывал на следующий правый узел. Если следующего правого узла нет, указатель next должен быть установлен в NULL. Изначально все указатели next установлены в NULL. Пример:
Input: root = [1,2,3,4,5,6,7]
Output: [1,#,2,3,#,4,5,6,7,#]
Explanation: Given the above perfect binary tree (Figure A), your function should populate each next pointer to point to its next right node, just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level.
👨‍💻 Алгоритм: 1️⃣Инициализируйте очередь Q, которую мы будем использовать во время обхода. Существует несколько способов реализации обхода в ширину, особенно когда речь идет о определении уровня конкретного узла. Можно добавлять в очередь пару (узел, уровень), и каждый раз, когда добавляются дети узла, мы добавляем (node.left, parent_level + 1) и (node.right, parent_level + 1). Этот подход не будет очень эффективен для нашего алгоритма, так как нам нужны все узлы на одном уровне, и для этого потребуется еще одна структура данных. 2️⃣Более эффективный с точки зрения памяти способ разделения узлов одного уровня заключается в использовании некоторой разграничительной метки между уровнями. Обычно мы вставляем в очередь элемент NULL, который отмечает конец предыдущего уровня и начало следующего. Это отличный подход, но он все равно потребует некоторого количества памяти, пропорционально количеству уровней в дереве. 3️⃣Подход, который мы будем использовать здесь, будет иметь структуру вложенных циклов, чтобы обойти необходимость указателя NULL. По сути, на каждом шаге мы записываем размер очереди, который всегда соответствует всем узлам на определенном уровне. Как только мы узнаем этот размер, мы обрабатываем только это количество элементов и не более. К моменту, когда мы закончим обработку заданного количества элементов, в очереди будут все узлы следующего уровня. Вот псевдокод для этого:
while (!Q.empty())
{
    size = Q.size()
    for i in range 0..size
    {
        node = Q.pop()
        Q.push(node.left)
        Q.push(node.right)
    }
}
Мы начинаем с добавления корня дерева в очередь. Поскольку на уровне 0 есть только один узел, нам не нужно устанавливать какие-либо соединения, и мы можем перейти к циклу while. 😎 Решение:
public class Solution {
    public Node Connect(Node root) {
        if (root == null) {
            return root;
        }

        Queue<Node> Q = new Queue<Node>();
        Q.Enqueue(root);

        while (Q.Count > 0) {
            int size = Q.Count;

            for (int i = 0; i < size; i++) {
                Node node = Q.Dequeue();

                if (i < size - 1) {
                    node.next = Q.Peek();
                }

                if (node.left != null) {
                    Q.Enqueue(node.left);
                }

                if (node.right != null) {
                    Q.Enqueue(node.right);
                }
            }
        }

        return root;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

👩‍💻 Программирование теперь в телеграм! Вот обширная база материалов, которая ежедневно обновляется, выбирай своё направлен
👩‍💻 Программирование теперь в телеграм! Вот обширная база материалов, которая ежедневно обновляется, выбирай своё направление: Обучение JavaScript с нуля Обучение Python с нуля Обучение Java с нуля Обучение HTML/CSS с нуля Обучение C/С++ с нуля Обучение С# с нуля Обучение SQL/GO/PHP с нуля Обучение Kotlin/Swift с нуля Архив на 3489ГБ: Курсы, книги, шпаргалки, статьи, видео, ресурсы — всё собрано в одном месте: @roadmap_ready

#hard Задача: 115. Distinct Subsequences "Даны две строки s и t. Верните количество различных подпоследовательностей строки s, которые равны строке t. Тестовые примеры сгенерированы таким образом, что ответ укладывается в 32-битное целое число со знаком." Пример:
Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from s.
rabbbit
rabbbit
rabbbit
👨‍💻 Алгоритм: 1️⃣Определите функцию с названием recurse, которая принимает два целочисленных значения i и j. Первое значение представляет текущий обрабатываемый символ в строке S, а второе - текущий символ в строке T. Инициализируйте словарь под названием memo, который будет кэшировать результаты различных вызовов рекурсии.** 2️⃣Проверьте базовый случай. Если одна из строк закончилась, возвращается 0 или 1 в зависимости от того, удалось ли обработать всю строку T или нет. Есть ещё один базовый случай, который следует рассмотреть. Если оставшаяся длина строки S меньше, чем у строки T, то совпадение невозможно. Если это обнаруживается, то рекурсия также обрезается и возвращается 0.** 3️⃣Затем проверяем, существует ли текущая пара индексов в нашем словаре. Если да, то просто возвращаем сохранённое/кэшированное значение. Если нет, то продолжаем обычную обработку. Сравниваем символы s[i] и t[j]. Сохраняем результат вызова recurse(i + 1, j) в переменную. Как упоминалось ранее, результат этой рекурсии необходим, независимо от совпадения символов. Если символы совпадают, добавляем к переменной результат вызова recurse(i + 1, j + 1). Наконец, сохраняем значение этой переменной в словаре с ключом (i, j) и возвращаем это значение в качестве ответа. 😎 Решение:
public class Solution {
    private Dictionary<string, int> memo;

    public int NumDistinct(string s, string t) {
        if (s.Length < t.Length)
            return 0;
        if (s == t || t == "")
            return 1;
        memo = new Dictionary<string, int>();
        return DistinctHelper(s.Substring(0, s.Length - 1), t) +
               ((s[s.Length - 1] == t[t.Length - 1])
                    ? DistinctHelper(s.Substring(0, s.Length - 1),
                                     t.Substring(0, t.Length - 1))
                    : 0);
    }

    private int DistinctHelper(string s, string t) {
        if (memo.ContainsKey(s + "," + t))
            return memo[s + "," + t];
        if (s.Length < t.Length)
            return 0;
        if (s == t || t == "")
            return 1;
        memo[s + "," + t] = DistinctHelper(s.Substring(0, s.Length - 1), t) +
                            ((s[s.Length - 1] == t[t.Length - 1])
                                 ? DistinctHelper(s.Substring(0, s.Length - 1),
                                                  t.Substring(0, t.Length - 1))
                                 : 0);
        return memo[s + "," + t];
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

#Medium Задача: 114. Flatten Binary Tree to Linked List "Связный список" должен использовать тот же класс TreeNode, где указатель на правого ребенка указывает на следующий узел в списке, а указатель на левого ребенка всегда равен null. "Связный список" должен быть в том же порядке, что и обход бинарного дерева в прямом порядке. Пример:
Input: root = [1,2,5,3,4,null,6]
Output: [1,null,2,null,3,null,4,null,5,null,6]
👨‍💻 Алгоритм: 1️⃣Плоское преобразование дерева: Рекурсивно преобразуем левое и правое поддеревья заданного узла, сохраняя соответствующие конечные узлы в leftTail и rightTail. 2️⃣Установка соединений: Если у текущего узла есть левый ребенок, выполняем следующие соединения: leftTail.right = node.right node.right = node.left node.left = None 3️⃣Возврат конечного узла: Возвращаем rightTail, если у узла есть правый ребенок, иначе возвращаем leftTail. 😎 Решение:
public class Solution {
    private TreeNode FlattenTree(TreeNode node) {
        if (node == null) {
            return null;
        }
        if (node.left == null && node.right == null) {
            return node;
        }
        TreeNode leftTail = FlattenTree(node.left);
        TreeNode rightTail = FlattenTree(node.right);
        if (leftTail != null) {
            leftTail.right = node.right;
            node.right = node.left;
            node.left = null;
        }
        return rightTail == null ? leftTail : rightTail;
    }

    public void Flatten(TreeNode root) {
        FlattenTree(root);
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

🌊 Водоворот знаний в Кодовороте 🤿 Погружайся в мир лучших видео уроков по программированию. Каждый день на канале выходит п
🌊  Водоворот знаний в Кодовороте 🤿 Погружайся в мир лучших видео уроков по программированию. Каждый день на канале выходит полезный контент. Кодируй своё будущее вместе с нами!Подпишись

#Medium Задача: 113. Path Sum II Дан корень бинарного дерева и целое число targetSum. Верните все пути от корня до листа, где сумма значений узлов в пути равна targetSum. Каждый путь должен быть возвращён как список значений узлов, а не ссылок на узлы. Путь от корня до листа — это путь, начинающийся от корня и заканчивающийся на любом листовом узле. Лист — это узел без детей. Пример:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: [[5,4,11,2],[5,8,4,5]]
Explanation: There are two paths whose sum equals targetSum:
5 + 4 + 11 + 2 = 22
5 + 8 + 4 + 5 = 22
👨‍💻 Алгоритм: 1️⃣Определение функции recurseTree: Функция принимает текущий узел (node), оставшуюся сумму (remainingSum), которая необходима для продолжения поиска вниз по дереву, и список узлов (pathNodes), который содержит все узлы, встреченные до текущего момента на данной ветке. 2️⃣Проверка условий: На каждом шаге проверяется, равна ли оставшаяся сумма значению текущего узла. Если это так и текущий узел является листом, текущий путь (pathNodes) добавляется в итоговый список путей, который должен быть возвращен. 3️⃣Обработка всех ветвей: Учитывая, что значения узлов могут быть отрицательными, необходимо исследовать все ветви дерева до самых листьев, независимо от текущей суммы по пути. 😎 Решение:
public class Solution {
    private void RecurseTree(TreeNode node, int remainingSum,
                             List<int> pathNodes, IList<IList<int>> pathsList) {
        if (node == null) {
            return;
        }

        pathNodes.Add(node.val);
        if (remainingSum == node.val && node.left == null && node.right == null) {
            pathsList.Add(new List<int>(pathNodes));
        } else {
            this.RecurseTree(node.left, remainingSum - node.val, pathNodes, pathsList);
            this.RecurseTree(node.right, remainingSum - node.val, pathNodes, pathsList);
        }

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

    public IList<IList<int>> PathSum(TreeNode root, int sum) {
        List<IList<int>> pathsList = new List<IList<int>>();
        List<int> pathNodes = new List<int>();
        this.RecurseTree(root, sum, pathNodes, pathsList);
        return pathsList;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

#easy Задача: 112. Path Sum Дан корень бинарного дерева и целое число targetSum. Верните true, если в дереве существует путь от корня до листа, такой, что сумма всех значений вдоль пути равна targetSum. Лист — это узел без детей. Пример:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
Output: true
Explanation: The root-to-leaf path with the target sum is shown.
👨‍💻 Алгоритм: 1️⃣Инициализация стека: Начать с помещения в стек корневого узла и соответствующей оставшейся суммы, равной sum - root.val. 2️⃣Обработка узлов: Извлечь текущий узел из стека и вернуть True, если оставшаяся сумма равна 0 и узел является листом. 3️⃣Добавление дочерних узлов в стек: Если оставшаяся сумма не равна нулю или узел не является листом, добавить в стек дочерние узлы с соответствующими оставшимися суммами. 😎 Решение:
public class Solution {
    public bool HasPathSum(TreeNode root, int sum) {
        if (root == null)
            return false;
        Stack<TreeNode> nodeStack = new Stack<TreeNode>();
        Stack<int> sumStack = new Stack<int>();
        nodeStack.Push(root);
        sumStack.Push(sum - root.val);
        while (nodeStack.Count > 0) {
            TreeNode node = nodeStack.Pop();
            int currSum = sumStack.Pop();
            if (node.left == null && node.right == null && currSum == 0)
                return true;
            if (node.left != null) {
                nodeStack.Push(node.left);
                sumStack.Push(currSum - node.left.val);
            }

            if (node.right != null) {
                nodeStack.Push(node.right);
                sumStack.Push(currSum - node.right.val);
            }
        }

        return false;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

👨‍💻 Чтобы стать более востребованным перцем в IT индустрии неплохо бы знать английский. Канал Hello Word в игровом стиле по
👨‍💻 Чтобы стать более востребованным перцем в IT индустрии неплохо бы знать английский. Канал Hello Word в игровом стиле поможет улучшить твой English skill. 🤓У нас ты найдешь: 🟡 Тесты с пропуском слов 🟡 Мемы на английском 🟡 Полезные шпаргалки для изучения 😎 Расширяй свои навыки и покоряй начинай покорять западную индустрию. Испытай свои знания и попробуй пройти тест.

#easy Задача: 111. Minimum Depth of Binary Tree Дано бинарное дерево, найдите его минимальную глубину. Минимальная глубина - это количество узлов вдоль самого короткого пути от корневого узла до ближайшего листового узла. Пример:
Input: root = [3,9,20,null,null,15,7]
Output: 2
👨‍💻 Алгоритм: 1️⃣Мы будем использовать метод обхода в глубину (dfs) с корнем в качестве аргумента. Базовое условие рекурсии: это для узла NULL, в этом случае мы должны вернуть 0. 2️⃣Если левый ребенок корня является NULL: тогда мы должны вернуть 1 + минимальную глубину для правого ребенка корневого узла, что равно 1 + dfs(root.right). 3️⃣Если правый ребенок корня является NULL: тогда мы должны вернуть 1 + минимальную глубину для левого ребенка корневого узла, что равно 1 + dfs(root.left). Если оба ребенка не являются NULL, тогда вернуть 1 + min(dfs(root.left), dfs(root.right)). 😎 Решение:
public class Solution {
    private int Dfs(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (root.left == null) {
            return 1 + Dfs(root.right);
        } else if (root.right == null) {
            return 1 + Dfs(root.left);
        }
        return 1 + Math.Min(Dfs(root.left), Dfs(root.right));
    }

    public int MinDepth(TreeNode root) {
        return Dfs(root);
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

#easy Задача: 110. Balanced Binary Tree Дано бинарное дерево, определите, является ли оно сбалансированным по высоте. Пример:
Input: root = [3,9,20,null,null,15,7]
Output: true
👨‍💻 Алгоритм: 1️⃣Сначала мы определяем функцию height, которая для любого узла p в дереве T возвращает: -1, если p является пустым поддеревом, т.е. null; 1 + max(height(p.left), height(p.right)) в противном случае. 2️⃣Теперь, когда у нас есть метод для определения высоты дерева, остается только сравнить высоты детей каждого узла. Дерево T с корнем r является сбалансированным тогда и только тогда, когда высоты его двух детей отличаются не более чем на 1 и поддеревья каждого ребенка также сбалансированы. 3️⃣Следовательно, мы можем сравнить высоты двух дочерних поддеревьев, а затем рекурсивно проверить каждое из них: Если root == NULL, возвращаем true. Если abs(height(root.left) - height(root.right)) > 1, возвращаем false. В противном случае возвращаем isBalanced(root.left) && isBalanced(root.right). 😎 Решение:
public class Solution {
    private int Height(TreeNode root) {
        if (root == null) {
            return -1;
        }
        return 1 + Math.Max(Height(root.left), Height(root.right));
    }

    public bool IsBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        return Math.Abs(Height(root.left) - Height(root.right)) < 2 &&
               IsBalanced(root.left) && IsBalanced(root.right);
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

💸 Работа для IT'шников Выбери своё направление ⤵ 1. Frontend 2. Python 3. Java 4. Тестировщик QA 5. Data Science 6. DevOps 7. C# 8. С/C++ 9. Golang 10. PHP 11. Kotlin 12. Swift

#medium Задача: 109. Convert Sorted List to Binary Search Tree Дана голова односвязного списка, элементы которого отсортированы в порядке возрастания. Преобразуйте его в сбалансированное по высоте бинарное дерево поиска. Пример:
Input: head = [-10,-3,0,5,9]
Output: [0,-3,9,-10,null,5]
Explanation: One possible answer is [0,-3,9,-10,null,5], which represents the shown height balanced BST.
👨‍💻 Алгоритм: 1️⃣Поскольку нам дан односвязный список, а не массив, у нас нет прямого доступа к элементам списка по индексам. Нам нужно определить средний элемент односвязного списка. Мы можем использовать подход с двумя указателями для нахождения среднего элемента списка. В основном, у нас есть два указателя: slow_ptr и fast_ptr. slow_ptr перемещается на один узел за раз, тогда как fast_ptr перемещается на два узла за раз. К тому времени, как fast_ptr достигнет конца списка, slow_ptr окажется в середине списка. Для списка с четным количеством элементов любой из двух средних элементов может стать корнем BST. 2️⃣Как только мы находим средний элемент списка, мы отсоединяем часть списка слева от среднего элемента. Мы делаем это, сохраняя prev_ptr, который указывает на узел перед slow_ptr, т.е. prev_ptr.next = slow_ptr. Для отсоединения левой части мы просто делаем prev_ptr.next = None. 3️⃣Для создания сбалансированного по высоте BST нам нужно передать только голову связанного списка в функцию, которая преобразует его в BST. Таким образом, мы рекурсивно работаем с левой половиной списка, передавая оригинальную голову списка, и с правой половиной, передавая slow_ptr.next в качестве головы. 😎 Решение:
public class Solution {
    public TreeNode SortedListToBST(ListNode head) {
        if (head == null)
            return null;
        ListNode mid = FindMiddleElement(head);
        TreeNode node = new TreeNode(mid.val);
        if (head == mid)
            return node;
        node.left = this.SortedListToBST(head);
        node.right = this.SortedListToBST(mid.next);
        return node;
    }

    private ListNode FindMiddleElement(ListNode head) {
        ListNode prevPtr = null;
        ListNode slowPtr = head;
        ListNode fastPtr = head;
        while (fastPtr != null && fastPtr.next != null) {
            prevPtr = slowPtr;
            slowPtr = slowPtr.next;
            fastPtr = fastPtr.next.next;
        }

        if (prevPtr != null)
            prevPtr.next = null;
        return slowPtr;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

#easy Задача: 108. Convert Sorted Array to Binary Search Tree Дан массив целых чисел nums, элементы которого отсортированы в порядке возрастания. Преобразуйте его в сбалансированное по высоте двоичное дерево поиска. Пример:
Input: nums = [-10,-3,0,5,9]
Output: [0,-3,9,-10,null,5]
Explanation: [0,-10,5,null,-3,null,9] is also accepted:
👨‍💻 Алгоритм: 1️⃣Инициализация функции помощника: Реализуйте функцию помощника helper(left, right), которая строит двоичное дерево поиска (BST) из элементов массива nums между индексами left и right. Если left > right, это означает, что элементов для построения поддерева нет, возвращаем None. 2️⃣Выбор корня и разделение массива: Выберите элемент в середине для корня: p = (left + right) // 2. Инициализируйте корень: root = TreeNode(nums[p]). 3️⃣Рекурсивное построение поддеревьев: Рекурсивно стройте левое поддерево: root.left = helper(left, p - 1). Рекурсивно стройте правое поддерево: root.right = helper(p + 1, right). В качестве результата возвращайте helper(0, len(nums) - 1), начиная с корня дерева. 😎 Решение:
public class Solution {
    public TreeNode SortedArrayToBST(int[] nums) {
        return helper(nums, 0, nums.Length - 1);
    }

    public TreeNode helper(int[] nums, int left, int right) {
        if (left > right) {
            return null;
        }

        int p = (left + right) / 2;
        TreeNode root = new TreeNode(nums[p]);
        root.left = helper(nums, left, p - 1);
        root.right = helper(nums, p + 1, right);
        return root;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

#medium Задача: 107. Binary Tree Level Order Traversal II Дан корень бинарного дерева. Верните обход значений узлов снизу вверх по уровням (то есть слева направо, уровень за уровнем, от листа к корню). Пример:
Input: root = [3,9,20,null,null,15,7]
Output: [[15,7],[9,20],[3]]
👨‍💻 Алгоритм: 1️⃣Инициализируйте список вывода levels. Длина этого списка определяет, какой уровень в данный момент обновляется. Вам следует сравнить этот уровень len(levels) с уровнем узла level, чтобы убедиться, что вы добавляете узел на правильный уровень. Если вы все еще находитесь на предыдущем уровне, добавьте новый уровень, вставив новый список в levels. 2️⃣Добавьте значение узла в последний уровень в levels. 3️⃣Рекурсивно обработайте дочерние узлы, если они не равны None, используя функцию helper(node.left / node.right, level + 1). 😎 Решение:
public class Solution {
    List<IList<int>> levels = new List<IList<int>>();

    public void Helper(TreeNode node, int level) {
        if (levels.Count == level)
            levels.Add(new List<int>());
        levels[level].Add(node.val);
        if (node.left != null)
            Helper(node.left, level + 1);
        if (node.right != null)
            Helper(node.right, level + 1);
    }

    public IList<IList<int>> LevelOrderBottom(TreeNode root) {
        if (root == null)
            return levels;
        Helper(root, 0);
        levels.Reverse();
        return levels;
    }
}
🔥 ТОП ВОПРОСОВ С СОБЕСОВ 🔒 База собесов | 🔒 База тестовых

🔥Тесты для подготовки к собеседованию🔥 Выбери своё направление: 1. Frontend 2. Python 3. Java 4. Тестировщик QA 5. Data Sci
🔥Тесты для подготовки к собеседованию🔥 Выбери своё направление: 1. Frontend 2. Python 3. Java 4. Тестировщик QA 5. Data Science 6. DevOps 7. C# 8. С/C++ 9. Golang 10. PHP 11. Kotlin 12. Swift