Java | LeetCode
6.5K subscribers
219 photos
1 video
1.44K links
Сайт: https://easyoffer.ru/
Все каналы: t.me/+xGeAw6ckJ4liYzQy

Контакт для рекламы: @easyoffer_adv
Download Telegram
Задача: 1328. Break a Palindrome
Сложность: medium

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

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

Строка a лексикографически меньше строки b (одинаковой длины), если в первой позиции, где они отличаются, у строки a символ строго меньше соответствующего символа в строке b. Например, "abcc" лексикографически меньше "abcd", потому что первой различающейся позицией является четвертая, и 'c' меньше, чем 'd'.

Пример:
Input: palindrome = "abccba"
Output: "aaccba"
Explanation: There are many ways to make "abccba" not a palindrome, such as "zbccba", "aaccba", and "abacba".
Of all the ways, "aaccba" is the lexicographically smallest.


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

1⃣Если длина строки равна 1, верните пустую строку, так как невозможно создать непалиндромическую строку в этом случае.

2⃣Итерируйтесь по строке слева до середины строки: если символ не равен 'a', измените его на 'a' и верните строку.

3⃣Если вы прошли всю левую часть строки и все еще не получили непалиндромическую строку, это означает, что строка состоит только из 'a'. Следовательно, измените последний символ на 'b' и верните полученную строку.

😎 Решение:
class Solution {
public String breakPalindrome(String palindrome) {
int length = palindrome.length();

if (length == 1) {
return "";
}

char[] palindromeArray = palindrome.toCharArray();
for (int i = 0; i < length / 2; i++) {
if (palindromeArray[i] != 'a') {
palindromeArray[i] = 'a';
return new String(palindromeArray);
}
}

palindromeArray[length - 1] = 'b';
return new String(palindromeArray);
}
}


Ставь 👍 и забирай 📚 Базу знаний
🔥1
Задача: 462. Minimum Moves to Equal Array Elements II
Сложность: medium

Дан массив целых чисел nums размера n, вернуть минимальное количество ходов, необходимых для того, чтобы сделать все элементы массива равными.

В одном ходе вы можете увеличить или уменьшить элемент массива на 1.

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

Пример:
Input: nums = [1,2,3]
Output: 2
Explanation:
Only two moves are needed (remember each move increments or decrements one element):
[1,2,3] => [2,2,3] => [2,2,2]


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

1⃣Найти минимальный и максимальный элементы в массиве. Пусть k будет числом, к которому должны быть приведены все элементы массива.

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

3⃣Определить минимальное количество ходов среди всех возможных k, что и будет конечным результатом.

😎 Решение:
public class Solution {
public int minMoves2(int[] nums) {
long ans = Long.MAX_VALUE;
int minval = Integer.MAX_VALUE;
int maxval = Integer.MIN_VALUE;
for (int num : nums) {
minval = Math.min(minval, num);
maxval = Math.max(maxval, num);
}
for (int i = minval; i <= maxval; i++) {
long sum = 0;
for (int num : nums) {
sum += Math.abs(num - i);
}
ans = Math.min(ans, sum);
}
return (int) ans;
}
}


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

Уродливое число — это положительное целое число, простые множители которого ограничены числами 2, 3 и 5.

Дано целое число n, верните true, если n является уродливым числом.

Пример:
Input: n = 6
Output: true
Explanation: 6 = 2 × 3


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

1⃣Если данное целое число n неположительное, верните false, так как неположительное число не может быть уродливым.

2⃣Определите функцию keepDividingWhenDivisible, которая принимает два аргумента: делимое и делитель. Эта функция будет делить делимое на делитель до тех пор, пока оно делится без остатка. Функция возвращает измененное делимое. Последовательно примените эту функцию к n с делителями 2, 3 и 5.

3⃣Если после всех делений n равно 1, верните true, иначе верните false.

😎 Решение:
class Solution {
public boolean isUgly(int n) {
if (n <= 0) {
return false;
}
for (int factor : new int[] {2, 3, 5}) {
n = keepDividingWhenDivisible(n, factor);
}
return n == 1;
}

private int keepDividingWhenDivisible(int dividend, int divisor) {
while (dividend % divisor == 0) {
dividend /= divisor;
}
return dividend;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 713. Subarray Product Less Than K
Сложность: medium

Если задан массив целых чисел nums и целое число k, верните количество смежных подмассивов, в которых произведение всех элементов в подмассиве строго меньше k.

Пример:
Input: nums = [10,5,2,6], k = 100
Output: 8


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

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

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

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

😎 Решение:
public class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int product = 1, count = 0, left = 0;
for (int right = 0; right < nums.length; right++) {
product *= nums[right];
while (product >= k) {
product /= nums[left];
left++;
}
count += right - left + 1;
}
return count;
}
}


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

Есть ряд из n домов, где каждый дом можно покрасить в один из трёх цветов: красный, синий или зелёный. Стоимость покраски каждого дома в определённый цвет разная. Необходимо покрасить все дома так, чтобы никакие два соседних дома не были окрашены в один и тот же цвет.

Стоимость покраски каждого дома в определённый цвет представлена в виде матрицы стоимости n x 3.

Например, costs[0][0] — это стоимость покраски дома 0 в красный цвет; costs[1][2] — это стоимость покраски дома 1 в зелёный цвет и так далее...
Верните минимальную стоимость покраски всех домов.

Пример:
Input: costs = [[17,2,17],[16,16,5],[14,3,19]]
Output: 10
Explanation: Paint house 0 into blue, paint house 1 into green, paint house 2 into blue.
Minimum cost: 2 + 5 + 3 = 10.


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

1⃣Инициализируйте массив dp размера n x 3 для хранения минимальных затрат на покраску домов. Установите начальные значения для первого дома: dp[0][0] = costs[0][0], dp[0][1] = costs[0][1], dp[0][2] = costs[0][2].

2⃣Для каждого дома i от 1 до n-1 обновите значения массива dp:
dp[i][0] = costs[i][0] + min(dp[i-1][1], dp[i-1][2])
dp[i][1] = costs[i][1] + min(dp[i-1][0], dp[i-1][2])
dp[i][2] = costs[i][2] + min(dp[i-1][0], dp[i-1][1])

3⃣Верните минимальное значение из последней строки массива dp: min(dp[n-1][0], dp[n-1][1], dp[n-1][2]).

😎 Решение:
class Solution {
public int minCost(int[][] costs) {
int n = costs.length;
int[][] dp = new int[n][3];
dp[0] = costs[0].clone();

for (int i = 1; i < n; i++) {
dp[i][0] = costs[i][0] + Math.min(dp[i-1][1], dp[i-1][2]);
dp[i][1] = costs[i][1] + Math.min(dp[i-1][0], dp[i-1][2]);
dp[i][2] = costs[i][2] + Math.min(dp[i-1][0], dp[i-1][1]);
}

return Math.min(dp[n-1][0], Math.min(dp[n-1][1], dp[n-1][2]));
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍1🔥1
Задача: 339. Nested List Weight Sum
Сложность: medium

Вам дан вложенный список целых чисел nestedList. Каждый элемент либо целое число, либо список, элементы которого также могут быть целыми числами или другими списками.

Глубина целого числа — это количество списков, в которых оно находится. Например, вложенный список [1,[2,2],[[3],2],1] имеет значения каждого целого числа, установленные в его глубину.

Верните сумму каждого целого числа в nestedList, умноженного на его глубину.

Пример:
Input: nestedList = [1,[4,[6]]]
Output: 27
Explanation: One 1 at depth 1, one 4 at depth 2, and one 6 at depth 3. 1*1 + 4*2 + 6*3 = 27.


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

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

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

3⃣ Возврат результата:
Возвращайте общую сумму на каждом уровне рекурсии. Основная функция возвращает итоговую сумму.

😎 Решение:
class Solution {

public int depthSum(List<NestedInteger> nestedList) {
return dfs(nestedList, 1);
}

private int dfs(List<NestedInteger> list, int depth) {
int total = 0;
for (NestedInteger nested : list) {
if (nested.isInteger()) {
total += nested.getInteger() * depth;
} else {
total += dfs(nested.getList(), depth + 1);
}
}
return total;
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍2
Задача: 1165. Single-Row Keyboard
Сложность: easy

Есть специальная клавиатура, на которой все клавиши расположены в один ряд.

Дана строка keyboard длиной 26, указывающая на раскладку клавиатуры (индексирована от 0 до 25). Изначально ваш палец находится на индексе 0. Чтобы напечатать символ, нужно переместить палец на индекс нужного символа. Время, затраченное на перемещение пальца с индекса i на индекс j, равно |i - j|.

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

Пример:
Input: keyboard = "abcdefghijklmnopqrstuvwxyz", word = "cba"
Output: 4
Explanation: The index moves from 0 to 2 to write 'c' then to 1 to write 'b' then to 0 again to write 'a'.
Total time = 2 + 1 + 1 = 4.


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

1⃣Клавиатура содержит уникальные строчные английские буквы, поэтому мы можем сопоставить её с массивом размера 26. Создадим массив размера 26, назовём его keyIndices. Сохраните индекс каждой буквы в этом массиве, проходя по строке keyboard. Инициализируйте переменную result значением 0, которая будет хранить сумму всех расстояний. Объявите переменную prev, которая будет хранить индекс предыдущей клавиши. Поскольку начальная позиция равна 0, инициализируйте её значением 0.

2⃣Проходите по строке word буква за буквой. Для каждой буквы c добавьте ∣prev−indexOf(c)∣ к result. Обновите prev до индекса c.

3⃣Повторите шаги 6 и 7 для всех букв. В конце прохода result будет содержать итоговое время, необходимое для набора слова.

😎 Решение:
class Solution {
public int calculateTime(String keyboard, String word) {
int[] keyIndices = new int[26];
for (int i = 0; i < keyboard.length(); i++) {
keyIndices[keyboard.charAt(i) - 'a'] = i;
}

int prev = 0;
int result = 0;

for (char c : word.toCharArray()) {
int index = keyIndices[c - 'a'];
result += Math.abs(prev - index);
prev = index;
}

return result;
}
}


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

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

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


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

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

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

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

😎 Решение:
import java.util.*;

public class Solution {
public int numSquarefulPerms(int[] nums) {
Arrays.sort(nums);
boolean[] used = new boolean[nums.length];
Set<List<Integer>> result = new HashSet<>();
List<Integer> path = new ArrayList<>();
backtrack(nums, used, path, result);
return result.size();
}

private void backtrack(int[] nums, boolean[] used, List<Integer> path, Set<List<Integer>> result) {
if (path.size() == nums.length) {
if (isSquareful(path)) {
result.add(new ArrayList<>(path));
}
return;
}

for (int i = 0; i < nums.length; i++) {
if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])) continue;
path.add(nums[i]);
used[i] = true;
backtrack(nums, used, path, result);
path.remove(path.size() - 1);
used[i] = false;
}
}

private boolean isSquareful(List<Integer> perm) {
for (int i = 0; i < perm.size() - 1; i++) {
int sum = perm.get(i) + perm.get(i + 1);
int root = (int) Math.sqrt(sum);
if (root * root != sum) return false;
}
return true;
}
}


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

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

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

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


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

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

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

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

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

int maxGap = 0;

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

return maxGap;
}
}


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

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

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


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

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

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

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

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

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

public boolean insertFront(int value) {
if (isFull()) return false;
front = (front - 1 + capacity) % capacity;
deque[front] = value;
size++;
return true;
}

public boolean insertLast(int value) {
if (isFull()) return false;
deque[rear] = value;
rear = (rear + 1) % capacity;
size++;
return true;
}

public boolean deleteFront() {
if (isEmpty()) return false;
front = (front + 1) % capacity;
size--;
return true;
}

public boolean deleteLast() {
if (isEmpty()) return false;
rear = (rear - 1 + capacity) % capacity;
size--;
return true;
}

public int getFront() {
if (isEmpty()) return -1;
return deque[front];
}

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

public boolean isEmpty() {
return size == 0;
}

public boolean isFull() {
return size == capacity;
}
}


Ставь 👍 и забирай 📚 Базу знаний
🔥1
Задача: 439. Ternary Expression Parser
Сложность: medium

Дана строка expression, представляющая произвольно вложенные тернарные выражения, вычислите это выражение и верните его результат.

Можно всегда считать, что данное выражение является корректным и содержит только цифры, '?', ':', 'T' и 'F', где 'T' означает истину, а 'F' - ложь. Все числа в выражении являются однозначными числами (т.е. в диапазоне от 0 до 9).

Условные выражения группируются справа налево (как обычно в большинстве языков), и результат выражения всегда будет либо цифрой, либо 'T', либо 'F'.

Пример:
Input: expression = "T?2:3"
Output: "2"
Explanation: If true, then result is 2; otherwise result is 3.


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

1⃣Определите вспомогательную функцию isValidAtomic(s), которая принимает строку s и возвращает True, если s является допустимым атомарным выражением. В противном случае функция возвращает False. Функция будет вызываться только с пятисимвольными строками. Если все следующие условия выполнены, функция возвращает True, иначе - False: s[0] является T или F. s[1] является ?. s[2] является T, F или цифрой от 0 до 9. s[3] является :. s[4] является T, F или цифрой от 0 до 9.

2⃣Определите вспомогательную функцию solveAtomic(s), которая принимает строку s и возвращает значение атомарного выражения. Значение атомарного выражения равно E1, если B - это T, иначе значение равно E2. Функция будет вызываться только с пятисимвольными строками и возвращать один символ:.

3⃣Если s[0] является T, функция возвращает s[2], иначе возвращает s[4]. В функции parseTernary(expression) уменьшайте выражение до тех пор, пока не останется односимвольная строка. Инициализируйте j как expression.size() - 1 (это будет самый правый индекс окна). Пока самое правое окно длиной 5 не является допустимым атомарным выражением, уменьшайте j на 1. Когда будет найдено самое правое допустимое атомарное выражение, решите его и уменьшите до одного символа. Замените самое правое допустимое атомарное выражение одним символом, после чего длина выражения уменьшится на 4. В итоге останется односимвольная строка, которую и верните.

😎 Решение:
class Solution {
public String parseTernary(String expression) {
boolean isValidAtomic(String s) {
return s.charAt(0) == 'T' || s.charAt(0) == 'F' &&
s.charAt(1) == '?' &&
"TF0123456789".indexOf(s.charAt(2)) != -1 &&
s.charAt(3) == ':' &&
"TF0123456789".indexOf(s.charAt(4)) != -1;
}

char solveAtomic(String s) {
return s.charAt(0) == 'T' ? s.charAt(2) : s.charAt(4);
}

while (expression.length() != 1) {
int j = expression.length() - 1;
while (!isValidAtomic(expression.substring(j-4, j+1))) {
j -= 1;
}
expression = expression.substring(0, j-4) + solveAtomic(expression.substring(j-4, j+1)) + expression.substring(j+1);
}

return expression;
}
}


Ставь 👍 и забирай 📚 Базу знаний
🔥1
Задача: 786. K-th Smallest Prime Fraction
Сложность: medium

Вам дан отсортированный массив целых чисел arr, содержащий 1 и простые числа, где все элементы массива arr уникальны. Также дано целое число k.

Для каждого i и j, где 0 <= i < j < arr.length, мы рассматриваем дробь arr[i] / arr[j].

Верните k-ую наименьшую дробь из рассмотренных. Верните ответ в виде массива из двух целых чисел размера 2, где answer[0] == arr[i] и answer[1] == arr[j].

Пример:
Input: arr = [1,2,3,5], k = 3
Output: [2,5]
Explanation: The fractions to be considered in sorted order are:
1/5, 1/3, 2/5, 1/2, 3/5, and 2/3.
The third fraction is 2/5.


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

1⃣Инициализируйте пустую приоритетную очередь pq для хранения пар дробей и соответствующих им индексов. Итеративно пройдите по входному массиву arr, используя переменную цикла i. Для каждого элемента arr[i] вычислите дробь, образованную делением его на наибольший элемент в массиве (arr[arr.size() - 1]). Поместите пару, состоящую из отрицательного значения дроби (-1.0 * arr[i] / arr[arr.size() - 1]) и соответствующих индексов (i для числителя и arr.size() - 1 для знаменателя), в приоритетную очередь pq. Приоритетная очередь pq теперь содержит все дроби, образованные делением каждого элемента на наибольший элемент в массиве, отсортированные в порядке возрастания значений дробей.

2⃣Повторите следующие шаги k - 1 раз: удалите верхний элемент (наименьшую дробь) из приоритетной очереди pq и сохраните его индексы в переменной cur. Уменьшите индекс знаменателя (cur[1]--). Вычислите новую дробь, образованную делением числителя в cur[0] на уменьшенный знаменатель (arr[cur[1]]). Поместите новое значение дроби (-1.0 * arr[cur[0]] / arr[cur[1]]) и соответствующие индексы (cur[0] для числителя и cur[1] для знаменателя) в приоритетную очередь pq. После k - 1 итераций верхний элемент приоритетной очереди pq будет k-й наименьшей дробью.

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

😎 Решение:
import java.util.PriorityQueue;

class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> Double.compare((double)arr[a[0]] / arr[a[1]], (double)arr[b[0]] / arr[b[1]]));

for (int i = 0; i < arr.length - 1; i++) {
pq.offer(new int[]{i, arr.length - 1});
}

for (int i = 0; i < k - 1; i++) {
int[] cur = pq.poll();
if (cur[1] - 1 > cur[0]) {
pq.offer(new int[]{cur[0], cur[1] - 1});
}
}

int[] result = pq.poll();
return new int[]{arr[result[0]], arr[result[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⃣Пройти по строке и найти минимальное количество операций, чтобы сделать всю строку монотонной.

😎 Решение:
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.charAt(i) == '1' ? 1 : 0);
}

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

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


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

Всего есть numCourses курсов, которые вы должны пройти, пронумерованных от 0 до numCourses - 1. Вам дан массив prerequisites, где prerequisites[i] = [ai, bi] указывает на то, что вы должны сначала пройти курс bi, если хотите взять курс ai.
Например, пара [0, 1] указывает на то, что для прохождения курса 0 сначала нужно пройти курс 1.
Верните порядок курсов, которые вы должны пройти, чтобы завершить все курсы. Если существует несколько правильных ответов, верните любой из них. Если невозможно завершить все курсы, верните пустой массив.

Пример:
Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
Output: [0,2,1,3]
Объяснение: Всего есть 4 курса, которые нужно пройти. Чтобы взять курс 3, вы должны завершить оба курса 1 и 2. Оба курса 1 и 2 должны быть взяты после того, как вы завершите курс 0.
Таким образом, один из правильных порядков курсов — [0,1,2,3]. Другой правильный порядок — [0,2,1,3].


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

1⃣Инициализация и построение графа:
Инициализируйте стек S, который будет содержать топологически отсортированный порядок курсов в нашем графе.
Постройте список смежности, используя пары ребер, указанные на входе. Важно отметить, что пара вида [a, b] указывает на то, что курс b должен быть пройден, чтобы взять курс a. Это подразумевает ребро вида b ➔ a. Учтите это при реализации алгоритма.

2⃣Запуск поиска в глубину (DFS):
Для каждого узла в нашем графе выполните поиск в глубину (DFS), если этот узел еще не был посещен во время DFS другого узла.
Предположим, что мы выполняем поиск в глубину для узла N. Рекурсивно обойдите всех соседей узла N, которые еще не были обработаны.

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

😎 Решение:
class Solution {
static int WHITE = 1;
static int GRAY = 2;
static int BLACK = 3;

boolean isPossible;
Map<Integer, Integer> color;
Map<Integer, List<Integer>> adjList;
List<Integer> topologicalOrder;

private void init(int numCourses) {
this.isPossible = true;
this.color = new HashMap<>();
this.adjList = new HashMap<>();
this.topologicalOrder = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
this.color.put(i, WHITE);
}
}

private void dfs(int node) {
if (!this.isPossible) return;
this.color.put(node, GRAY);
for (Integer neighbor : this.adjList.getOrDefault(node, new ArrayList<>())) {
if (this.color.get(neighbor) == WHITE) {
this.dfs(neighbor);
} else if (this.color.get(neighbor) == GRAY) {
this.isPossible = false;
}
}
this.color.put(node, BLACK);
this.topologicalOrder.add(node);
}

public int[] findOrder(int numCourses, int[][] prerequisites) {
this.init(numCourses);
for (int i = 0; i < prerequisites.length; i++) {
int dest = prerequisites[i][0];
int src = prerequisites[i][1];
List<Integer> lst = adjList.getOrDefault(src, new ArrayList<>());
lst.add(dest);
adjList.put(src, lst);
}
for (int i = 0; i < numCourses; i++) {
if (this.color.get(i) == WHITE) {
this.dfs(i);
}
}
if (this.isPossible) {
int[] order = new int[numCourses];
for (int i = 0; i < numCourses; i++) {
order[i] = this.topologicalOrder.get(numCourses - i - 1);
}
return order;
} else {
return new int[0];
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: №21. Merge Two Sorted Lists
Сложность: easy

Вам даны заголовки двух отсортированных связанных списков list1 и list2.
Объедините их в один отсортированный список, сшивая существующие узлы.
Верните заголовок нового объединенного списка.

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


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

1⃣Если один из списков пуст — возвращаем второй как результат.

2⃣Сравниваем значения текущих узлов списков: выбираем меньший и рекурсивно вызываем mergeTwoLists для оставшейся части.

3⃣Связываем меньший узел с результатом рекурсивного вызова и возвращаем его как текущий.

😎 Решение:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 != null && list2 != null) {
if (list1.val < list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
return list1 != null ? list1 : list2;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 665. Non-decreasing Array
Сложность: medium

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

Мы определяем массив как неубывающий, если для каждого i (индексация с 0), такого что 0 <= i <= n - 2, выполняется условие nums[i] <= nums[i + 1].

Пример:
Input: nums = [4,2,3]
Output: true
Explanation: You could modify the first 4 to 1 to get a non-decreasing array.


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

1⃣Инициализация переменных:
Завести переменную count для подсчета числа изменений.
Проверить последовательность чисел в массиве nums.

2⃣Проверка условий:
Если nums[i] > nums[i + 1], то увеличиваем count.
Если count превышает 1, возвращаем false, так как больше одного изменения недопустимо.
Если nums[i - 1] > nums[i + 1] и nums[i] > nums[i + 2], то возвращаем false.

3⃣Возврат результата:
Если количество изменений не превышает 1, вернуть true.

😎 Решение:
public class Solution {
public boolean checkPossibility(int[] nums) {
int count = 0;

for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1]) {
if (count > 0) {
return false;
}
count++;
if (i == 1 || nums[i] >= nums[i - 2]) {
nums[i - 1] = nums[i];
} else {
nums[i] = nums[i - 1];
}
}
}

return true;
}
}


Ставь 👍 и забирай 📚 Базу знаний
🤔1
Задача: 1203. Sort Items by Groups Respecting Dependencies
Сложность: hard

Есть n предметов, каждый из которых принадлежит нулевой или одной из m групп, где group[i] — это группа, к которой принадлежит i-й предмет, и равно -1, если i-й предмет не принадлежит никакой группе. Предметы и группы имеют индексацию с нуля. Группа может не иметь ни одного предмета.

Верните отсортированный список предметов таким образом:
Предметы, принадлежащие одной группе, расположены рядом друг с другом в отсортированном списке.
Существуют некоторые отношения между этими предметами, где beforeItems[i] — это список, содержащий все предметы, которые должны быть перед i-м предметом в отсортированном массиве (слева от i-го предмета).
Верните любое решение, если существует более одного решения, и верните пустой список, если решения не существует.

Пример:
Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]]
Output: [6,3,4,1,5,2,0,7]


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

1⃣Инициализация и создание графов:
Присвоить уникальные идентификаторы группам для элементов без группы.
Создать два графа: item_graph для элементов и group_graph для групп. Также создать два массива для учета входящих рёбер для элементов и групп.

2⃣Построение графов:
Пройти по массиву beforeItems и добавить зависимости между элементами в item_graph, увеличивая счётчик входящих рёбер.
Если элементы принадлежат разным группам, добавить зависимость между группами в group_graph, увеличивая счётчик входящих рёбер.

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

😎 Решение:
class Solution {
public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
int groupId = m;
for (int i = 0; i < n; i++) if (group[i] == -1) group[i] = groupId++;

Map<Integer, List<Integer>> itemGraph = new HashMap<>();
Map<Integer, List<Integer>> groupGraph = new HashMap<>();
int[] itemIndegree = new int[n], groupIndegree = new int[groupId];
for (int i = 0; i < n; i++) itemGraph.put(i, new ArrayList<>());
for (int i = 0; i < groupId; i++) groupGraph.put(i, new ArrayList<>());

for (int curr = 0; curr < n; curr++) {
for (int prev : beforeItems.get(curr)) {
itemGraph.get(prev).add(curr);
itemIndegree[curr]++;
if (group[curr] != group[prev]) {
groupGraph.get(group[prev]).add(group[curr]);
groupIndegree[group[curr]]++;
}
}
}

List<Integer> itemOrder = topologicalSort(itemGraph, itemIndegree);
List<Integer> groupOrder = topologicalSort(groupGraph, groupIndegree);
if (itemOrder.isEmpty() || groupOrder.isEmpty()) return new int[0];

Map<Integer, List<Integer>> orderedGroups = new HashMap<>();
for (int item : itemOrder) orderedGroups.computeIfAbsent(group[item], k -> new ArrayList<>()).add(item);

List<Integer> answerList = new ArrayList<>();
for (int groupIndex : groupOrder) answerList.addAll(orderedGroups.getOrDefault(groupIndex, new ArrayList<>()));

return answerList.stream().mapToInt(Integer::intValue).toArray();
}

private List<Integer> topologicalSort(Map<Integer, List<Integer>> graph, int[] indegree) {
List<Integer> visited = new ArrayList<>();
Stack<Integer> stack = new Stack<>();
for (int key : graph.keySet()) if (indegree[key] == 0) stack.add(key);

while (!stack.isEmpty()) {
int curr = stack.pop();
visited.add(curr);
for (int next : graph.get(curr)) if (--indegree[next] == 0) stack.add(next);
}

return visited.size() == graph.size() ? visited : new ArrayList<>();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 1122. Relative Sort Array
Сложность: easy

Даны два массива arr1 и arr2, элементы arr2 уникальны, и все элементы arr2 также присутствуют в arr1.

Отсортируйте элементы arr1 таким образом, чтобы относительный порядок элементов в arr1 был таким же, как в arr2. Элементы, которые не встречаются в arr2, должны быть размещены в конце arr1 в порядке возрастания.

Пример:
Input: arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
Output: [2,2,2,1,4,3,3,9,6,7,19]


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

1⃣Инициализация и подсчёт:
Инициализируйте пустой массив result и массив remaining для хранения оставшихся элементов.
Создайте хеш-таблицу countMap для хранения количества вхождений каждого элемента из arr2 в arr1.

2⃣Заполнение countMap и remaining:
Пройдитесь по элементам arr1 и если элемент присутствует в countMap, увеличьте его счетчик. Если элемент не присутствует в arr2, добавьте его в remaining.

3⃣Формирование результирующего массива:
Пройдитесь по arr2 и добавьте элементы в result в соответствии с их количеством в countMap.
Отсортируйте массив remaining и добавьте его элементы в result.
Верните result в виде массива.

😎 Решение:
class Solution {

public int[] relativeSortArray(int[] arr1, int[] arr2) {
Map<Integer, Integer> countMap = new HashMap<>();
List<Integer> remaining = new ArrayList<>();
List<Integer> result = new ArrayList<>();

for (int value : arr2) {
countMap.put(value, 0);
}

for (int value : arr1) {
if (countMap.containsKey(value)) {
countMap.put(value, countMap.get(value) + 1);
} else {
remaining.add(value);
}
}

Collections.sort(remaining);

for (int value : arr2) {
for (int j = 0; j < countMap.get(value); j++) {
result.add(value);
}
}

result.addAll(remaining);

return result.stream().mapToInt(Integer::intValue).toArray();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 1382. Balance a Binary Search Tree
Сложность: medium

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

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

Пример:
Input: root = [1,null,2,null,3,null,4,null,null]
Output: [2,1,3,null,null,null,4]
Explanation: This is not the only correct answer, [3,1,4,null,2] is also correct.


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

1⃣Инициализация. Создайте пустой список inorder для хранения значений узлов после обхода в порядке возрастания.

2⃣Выполнение обхода в порядке возрастания. Обойдите бинарное дерево поиска (BST) и заполните список inorder значениями узлов в отсортированном порядке.

3⃣Перестроение сбалансированного BST. Определите рекурсивную функцию createBalancedBST, которая принимает список inorder, начальный индекс и конечный индекс в качестве параметров. Если начальный индекс больше конечного, верните null (или эквивалент). Вычислите средний индекс как середину текущего диапазона. Создайте новый узел дерева со значением в среднем индексе. Рекурсивно создайте левое поддерево, используя левую половину текущего диапазона. Рекурсивно создайте правое поддерево, используя правую половину текущего диапазона. Верните корень вновь построенного сбалансированного BST.

😎 Решение:
class Solution {
public TreeNode balanceBST(TreeNode root) {
if (root == null) return null;

TreeNode vineHead = new TreeNode(0);
vineHead.right = root;
TreeNode current = vineHead;

while (current.right != null) {
if (current.right.left != null) {
rightRotate(current, current.right);
} else {
current = current.right;
}
}

int nodeCount = 0;
current = vineHead.right;
while (current != null) {
nodeCount++;
current = current.right;
}

int m = (int) Math.pow(2, Math.floor(Math.log(nodeCount + 1) / Math.log(2))) - 1;
makeRotations(vineHead, nodeCount - m);
while (m > 1) {
m /= 2;
makeRotations(vineHead, m);
}

TreeNode balancedRoot = vineHead.right;
return balancedRoot;
}

private void rightRotate(TreeNode parent, TreeNode node) {
TreeNode tmp = node.left;
node.left = tmp.right;
tmp.right = node;
parent.right = tmp;
}

private void leftRotate(TreeNode parent, TreeNode node) {
TreeNode tmp = node.right;
node.right = tmp.left;
tmp.left = node;
parent.right = tmp;
}

private void makeRotations(TreeNode vineHead, int count) {
TreeNode current = vineHead;
for (int i = 0; i < count; i++) {
TreeNode tmp = current.right;
leftRotate(current, tmp);
current = current.right;
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Задача: 827. Making A Large Island
Сложность: hard

Вам дан n x n бинарный матрица grid. Вам разрешено изменить не более одного 0 на 1.

Верните размер самого большого острова в grid после выполнения этой операции.

Остров — это группа 1, соединенных в 4 направлениях.

Пример:
Input: grid = [[1,1],[1,0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 4.


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

1⃣Пройдите по матрице и пометьте каждую группу, используя уникальный индекс, и запомните её размер.

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

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

😎 Решение:
class Solution {
int[] dr = new int[]{-1, 0, 1, 0};
int[] dc = new int[]{0, -1, 0, 1};
int[][] grid;
int N;

public int largestIsland(int[][] grid) {
this.grid = grid;
N = grid.length;

int index = 2;
int[] area = new int[N*N + 2];
for (int r = 0; r < N; ++r)
for (int c = 0; c < N; ++c)
if (grid[r][c] == 1)
area[index] = dfs(r, c, index++);

int ans = 0;
for (int x: area) ans = Math.max(ans, x);
for (int r = 0; r < N; ++r)
for (int c = 0; c < N; ++c)
if (grid[r][c] == 0) {
Set<Integer> seen = new HashSet();
for (Integer move: neighbors(r, c))
if (grid[move / N][move % N] > 1)
seen.add(grid[move / N][move % N]);

int bns = 1;
for (int i: seen) bns += area[i];
ans = Math.max(ans, bns);
}

return ans;
}

public int dfs(int r, int c, int index) {
int ans = 1;
grid[r][c] = index;
for (Integer move: neighbors(r, c)) {
if (grid[move / N][move % N] == 1) {
grid[move / N][move % N] = index;
ans += dfs(move / N, move % N, index);
}
}

return ans;
}

public List<Integer> neighbors(int r, int c) {
List<Integer> ans = new ArrayList();
for (int k = 0; k < 4; ++k) {
int nr = r + dr[k];
int nc = c + dc[k];
if (0 <= nr && nr < N && 0 <= nc && nc < N)
ans.add(nr * N + nc);
}

return ans;
}


Ставь 👍 и забирай 📚 Базу знаний
💊1
Задача: 599. Minimum Index Sum of Two Lists
Сложность: easy

Даны два массива строк list1 и list2, необходимо найти общие строки с наименьшей суммой индексов.

Общая строка - это строка, которая появляется и в list1, и в list2.

Общая строка с наименьшей суммой индексов - это общая строка, такая, что если она появилась в list1[i] и list2[j], то i + j должно быть минимальным значением среди всех других общих строк.

Верните все общие строки с наименьшей суммой индексов. Верните ответ в любом порядке.

Пример:
Input: list1 = ["Shogun","Tapioca Express","Burger King","KFC"], list2 = ["Piatti","The Grill at Torrey Pines","Hungry Hunter Steakhouse","Shogun"]
Output: ["Shogun"]
Explanation: The only common string is "Shogun".


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

1⃣Для каждой строки из list1, сравниваем её с каждой строкой из list2, обходя весь список list2. Используем хэш-таблицу map, которая содержит элементы в виде (сумма: список строк). Здесь сумма относится к сумме индексов совпадающих элементов, а список строк соответствует списку совпадающих строк, чья сумма индексов равна этой сумме.

2⃣Во время сравнений, когда находится совпадение строки на i-м индексе из list1 и j-м индексе из list2, создаём запись в map, соответствующую сумме i + j, если такая запись ещё не существует. Если запись с этой суммой уже существует, добавляем текущую строку в список строк, соответствующих сумме i + j.

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

😎 Решение:
import java.util.*;

public class Solution {
public String[] findRestaurant(String[] list1, String[] list2) {
HashMap<Integer, List<String>> map = new HashMap<>();
for (int i = 0; i < list1.length; i++) {
for (int j = 0; j < list2.length; j++) {
if (list1[i].equals(list2[j])) {
if (!map.containsKey(i + j)) {
map.put(i + j, new ArrayList<>());
}
map.get(i + j).add(list1[i]);
}
}
}
int minIndexSum = Integer.MAX_VALUE;
for (int key : map.keySet()) {
minIndexSum = Math.min(minIndexSum, key);
}
return map.get(minIndexSum).toArray(new String[0]);
}
}


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