Java | LeetCode
7.08K subscribers
177 photos
1.06K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+icUwivvbGOkwNWRi
Вопросы собесов t.iss.one/+7ESm0VKXC4tjYzky
Вакансии t.iss.one/+4pspF5nDjgM4MjQy
Download Telegram
#medium
Задача: 33. Search in Rotated Sorted Array

Есть массив целых чисел nums, отсортированный в порядке возрастания (с уникальными значениями).

Перед передачей в вашу функцию массив nums может быть повёрнут в неизвестном индексе поворота k (1 <= k < nums.length), так что результирующий массив будет иметь вид [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексацией с нуля). Например, [0,1,2,4,5,6,7] может быть повёрнут в индексе поворота 3 и стать [4,5,6,7,0,1,2].

Для данного массива nums после возможного поворота и целого числа target, верните индекс target, если он есть в массиве, или -1, если его нет в массиве.

Вы должны написать алгоритм с временной сложностью O(log n).

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


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

1️⃣Выполните двоичный поиск для определения индекса поворота, инициализируя границы области поиска значениями left = 0 и right = n - 1. Пока left < right:
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.

2️⃣По завершении двоичного поиска мы имеем индекс поворота, обозначенный как pivot = left.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].

3️⃣Выполните двоичный поиск по подмассиву nums[0 ~ left - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс.
В противном случае выполните двоичный поиск по подмассиву nums[left ~ n - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс. В противном случае верните -1.

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

int answer = binarySearch(nums, 0, left - 1, target);
if (answer != -1) {
return answer;
}

return binarySearch(nums, left, n - 1, target);
}

private int binarySearch(int[] nums, int leftBoundary, int rightBoundary, int target) {
int left = leftBoundary, right = rightBoundary;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}

return -1;
}
}


🪙 1715 вопроса вопроса на Java разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#medium
Задача: 34. Find First and Last Position of Element in Sorted Array

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

Если целевое значение не найдено в массиве, верните [-1, -1].

Вы должны написать алгоритм со временной сложностью O(log n).

Пример:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]


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

1️⃣Определите функцию под названием findBound, которая принимает три аргумента: массив, целевое значение для поиска и булевое значение isFirst, указывающее, ищем ли мы первое или последнее вхождение цели.
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а end — в последний индекс массива.

2️⃣Мы итерируем, пока begin не станет больше, чем end.
На каждом шаге мы вычисляем средний элемент mid = (begin + end) / 2. Мы используем значение среднего элемента, чтобы решить, какую половину массива нам нужно искать.
Если nums[mid] == target:
Если isFirst true — это означает, что мы пытаемся найти первое вхождение элемента. Если mid == begin или nums[mid - 1] != target, тогда мы возвращаем mid как первое вхождение цели. В противном случае мы обновляем end = mid - 1.
Если isFirst false — это означает, что мы пытаемся найти последнее вхождение элемента. Если mid == end или nums[mid + 1] != target, тогда мы возвращаем mid как последнее вхождение цели. В противном случае мы обновляем begin = mid + 1.

3️⃣Если nums[mid] > target — мы обновляем end = mid - 1, так как мы должны отбросить правую сторону массива, поскольку средний элемент больше цели.
Если nums[mid] < target — мы обновляем begin = mid + 1, так как мы должны отбросить левую сторону массива, поскольку средний элемент меньше цели.
В конце нашей функции мы возвращаем значение -1, что указывает на то, что цель не найдена в массиве.
В основной функции searchRange мы сначала вызываем findBound с isFirst, установленным в true. Если это значение равно -1, мы можем просто вернуть [-1, -1]. В противном случае мы вызываем findBound с isFirst, установленным в false, чтобы получить последнее вхождение, а затем возвращаем результат.

😎 Решение:
class Solution {
public int[] searchRange(int[] nums, int target) {
int firstOccurrence = this.findBound(nums, target, true);

if (firstOccurrence == -1) {
return new int[] { -1, -1 };
}

int lastOccurrence = this.findBound(nums, target, false);

return new int[] { firstOccurrence, lastOccurrence };
}

private int findBound(int[] nums, int target, boolean isFirst) {
int N = nums.length;
int begin = 0, end = N - 1;

while (begin <= end) {
int mid = (begin + end) / 2;

if (nums[mid] == target) {
if (isFirst) {
if (mid == begin || nums[mid - 1] != target) {
return mid;
}
end = mid - 1;
} else {
if (mid == end || nums[mid + 1] != target) {
return mid;
}
begin = mid + 1;
}
} else if (nums[mid] > target) {
end = mid - 1;
} else {
begin = mid + 1;
}
}

return -1;
}
}


🪙 1715 вопроса вопроса на Java разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#easy
Задача: 35. Search Insert Position

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

Вы должны написать алгоритм со временной сложностью O(log n).

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


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

1️⃣Инициализируйте указатели left и right: left = 0, right = n - 1.

2️⃣Пока left <= right:
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
3️⃣Верните left.

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


🪙 1715 вопроса вопроса на Java разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
#medium
Задача: 36. Valid Sudoku

Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:

Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.

Пример:
Input: board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true


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

1️⃣Инициализируйте список из 9 хеш-множеств, где хеш-множество с индексом r будет использоваться для хранения ранее увиденных чисел в строке r судоку. Аналогично инициализируйте списки из 9 хеш-множеств для отслеживания столбцов и блоков.

2️⃣Итерируйтесь по каждой позиции (r, c) в судоку. На каждой итерации, если на текущей позиции есть число:
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.

3️⃣В противном случае обновите множество, отвечающее за отслеживание ранее увиденных чисел в текущей строке, столбце и блоке. Индекс текущего блока рассчитывается как (r / 3) * 3 + (c / 3), где / означает деление нацело.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.

😎 Решение:
class Solution {
public boolean isValidSudoku(char[][] board) {
int N = 9;

HashSet<Character>[] rows = new HashSet[N];
HashSet<Character>[] cols = new HashSet[N];
HashSet<Character>[] boxes = new HashSet[N];
for (int r = 0; r < N; r++) {
rows[r] = new HashSet<Character>();
cols[r] = new HashSet<Character>();
boxes[r] = new HashSet<Character>();
}

for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
char val = board[r][c];

if (val == '.') {
continue;
}

if (rows[r].contains(val)) {
return false;
}
rows[r].add(val);

if (cols[c].contains(val)) {
return false;
}
cols[c].add(val);

int idx = (r / 3) * 3 + c / 3;
if (boxes[idx].contains(val)) {
return false;
}
boxes[idx].add(val);
}
}
return true;
}
}


🪙 1715 вопроса вопроса на Java разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#hard
Задача: 37. Sudoku Solver

Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.

Решение Судоку должно удовлетворять всем следующим правилам:

Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.

Пример:
Input: board = 
[["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]]
Output:
[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]


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

1️⃣Теперь все готово для написания функции обратного поиска backtrack(row = 0, col = 0). Начните с верхней левой ячейки row = 0, col = 0. Продолжайте, пока не дойдете до первой свободной ячейки.

2️⃣Итерируйте по числам от 1 до 9 и попробуйте поставить каждое число d в ячейку (row, col).
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.

3️⃣Если вы на последней ячейке row == 8, col == 8:
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).

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

class Solution {
public void solveSudoku(char[][] board) {
int n = 3, N = n * n;
List<Map<Character, Integer>> track = new ArrayList<>(N * 3);
for (int i = 0; i < N * 3; i++) track.add(new HashMap<>());

for (int r = 0; r < N; r++) for (int c = 0; c < N; c++)
if (board[r][c] != '.') update(board, r, c, board[r][c], true, n, track);

backtrack(board, 0, 0, n, N, track);
}

private boolean backtrack(char[][] board, int r, int c, int n, int N, List<Map<Character, Integer>> track) {
if (c == N) { r++; c = 0; }
if (r == N) return true;

if (board[r][c] != '.') return backtrack(board, r, c + 1, n, N, track);

for (char num = '1'; num <= '9'; num++)
if (canPlace(num, r, c, n, track)) {
update(board, r, c, num, true, n, track);
if (backtrack(board, r, c + 1, n, N, track)) return true;
update(board, r, c, num, false, n, track);
}

return false;
}

private boolean canPlace(char num, int r, int c, int n, List<Map<Character, Integer>> track) {
int idx = r / n * n + c / n;
return track.get(r).getOrDefault(num, 0) == 0 && track.get(n + c).getOrDefault(num, 0) == 0
&& track.get(2 * n + idx).getOrDefault(num, 0) == 0;
}

private void update(char[][] board, int r, int c, char num, boolean place, int n, List<Map<Character, Integer>> track) {
int idx = r / n * n + c / n, delta = place ? 1 : -1;
track.get(r).put(num, track.get(r).getOrDefault(num, 0) + delta);
track.get(n + c).put(num, track.get(n + c).getOrDefault(num, 0) + delta);
track.get(2 * n + idx).put(num, track.get(2 * n + idx).getOrDefault(num, 0) + delta);
board[r][c] = place ? num : '.';
}
}


🪙 1715 вопроса вопроса на Java разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
#medium
Задача: 38. Count and Say

Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:

countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".

Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".

Пример:
Input: n = 4

Output: "1211"

Explanation:

countAndSay(1) = "1"
countAndSay(2) = RLE of "1" = "11"
countAndSay(3) = RLE of "11" = "21"
countAndSay(4) = RLE of "21" = "1211"


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

1️⃣Мы хотим использовать шаблон, который соответствует строкам из одинаковых символов, таких как "4", "7777", "2222222".
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.

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

3️⃣*: этот квалификатор, следующий за ссылкой на группу \1, указывает, что мы хотели бы видеть повторение группы ноль или более раз.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.

😎 Решение:
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Solution {
public String countAndSay(int n) {
String currentString = "1";
Pattern pattern = Pattern.compile("(.)\\1*");
for (int i = 1; i < n; ++i) {
Matcher m = pattern.matcher(currentString);
StringBuffer nextString = new StringBuffer();
while (m.find()) {
nextString.append(
m.group().length() + String.valueOf(m.group().charAt(0))
);
}
currentString = nextString.toString();
}
return currentString;
}
}


🪙 1715 вопроса вопроса на Java разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
#medium
Задача: 39. Combination Sum

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

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

Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.

Пример:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]


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

1️⃣Как видно, вышеописанный алгоритм обратного отслеживания разворачивается как обход дерева в глубину (DFS - Depth-First Search), который часто реализуется с помощью рекурсии.
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.

2️⃣Для первого базового случая рекурсивной функции, если remain == 0, то есть мы достигаем желаемой целевой суммы, поэтому мы можем добавить текущую комбинацию в итоговый список.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.

3️⃣Помимо вышеупомянутых двух базовых случаев, мы затем продолжаем исследовать подсписок кандидатов, начиная с [start ... n].
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.

😎 Решение:
class Solution {
protected void backtrack(
int remain,
LinkedList<Integer> comb,
int start,
int[] candidates,
List<List<Integer>> results
) {
if (remain == 0) {
results.add(new ArrayList<Integer>(comb));
return;
} else if (remain < 0) {
return;
}

for (int i = start; i < candidates.length; ++i) {
comb.add(candidates[i]);
this.backtrack(
remain - candidates[i],
comb,
i,
candidates,
results
);
comb.removeLast();
}
}

public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> results = new ArrayList<List<Integer>>();
LinkedList<Integer> comb = new LinkedList<Integer>();

this.backtrack(target, comb, 0, candidates, results);
return results;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#medium
Задача: 40. Combination Sum II

Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.

Каждое число в candidates может быть использовано только один раз в комбинации.

Примечание: Набор решений не должен содержать повторяющихся комбинаций.

Пример:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]


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

1️⃣Во-первых, мы создаём таблицу счётчиков из предоставленного списка чисел. Затем мы используем эту таблицу счётчиков в процессе обратного поиска, который мы определяем как функцию backtrack(comb, remain, curr, candidate_groups, results). Для сохранения состояния на каждом этапе обратного поиска мы используем несколько параметров в функции:
comb: комбинация, которую мы построили на данный момент.
remain: оставшаяся сумма, которую нам нужно заполнить, чтобы достичь целевой суммы.
curr: курсор, который указывает на текущую группу чисел, используемую из таблицы счётчиков.
counter: текущая таблица счётчиков.
results: окончательные комбинации, которые достигают целевой суммы.

2️⃣При каждом вызове функции обратного поиска мы сначала проверяем, достигли ли мы целевой суммы (то есть sum(comb) = target), и нужно ли прекратить исследование, потому что сумма текущей комбинации превышает желаемую целевую сумму.

3️⃣Если осталась сумма для заполнения, мы затем перебираем текущую таблицу счётчиков, чтобы выбрать следующего кандидата. После выбора кандидата мы продолжаем исследование, вызывая функцию backtrack() с обновлёнными состояниями. Более важно, что в конце каждого исследования нам нужно вернуть состояние, которое мы обновили ранее, чтобы начать с чистого листа для следующего исследования. Именно из-за этой операции обратного поиска алгоритм получил своё название.

😎 Решение:
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> results = new ArrayList<>();
LinkedList<Integer> comb = new LinkedList<>();

HashMap<Integer, Integer> counter = new HashMap<>();
for (int candidate : candidates) {
counter.put(candidate, counter.getOrDefault(candidate, 0) + 1);
}

List<int[]> counterList = new ArrayList<>();
counter.forEach((key, value) -> {
counterList.add(new int[] { key, value });
});

backtrack(comb, target, 0, counterList, results);
return results;
}

private void backtrack(
LinkedList<Integer> comb,
int remain,
int curr,
List<int[]> counter,
List<List<Integer>> results
) {
if (remain <= 0) {
if (remain == 0) {
results.add(new ArrayList<Integer>(comb));
}
return;
}

for (int nextCurr = curr; nextCurr < counter.size(); ++nextCurr) {
int[] entry = counter.get(nextCurr);
Integer candidate = entry[0], freq = entry[1];

if (freq <= 0) continue;

comb.addLast(candidate);
counter.set(nextCurr, new int[] { candidate, freq - 1 });

backtrack(comb, remain - candidate, nextCurr, counter, results);

counter.set(nextCurr, new int[] { candidate, freq });
comb.removeLast();
}
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#hard
Задача: 41. First Missing Positive

Дан неотсортированный массив целых чисел nums. Верните наименьшее положительное целое число, которого нет в массиве nums.

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

Пример:
Input: nums = [3,4,-1,1]
Output: 2
Explanation: 1 is in the array but 2 is missing.


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

1️⃣Инициализировать переменную n длиной массива nums. Создать массив seen размером n + 1. Отметить элементы в массиве nums как просмотренные в массиве seen.
Для каждого числа num в массиве nums, если num больше 0 и меньше или равно n, установить seen[num] в значение true.

2️⃣Найти наименьшее недостающее положительное число:
Проитерировать от 1 до n, и если seen[i] не равно true, вернуть i как наименьшее недостающее положительное число.

3️⃣Если массив seen содержит все элементы от 1 до n, вернуть n + 1 как наименьшее недостающее положительное число.

😎 Решение:
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
boolean[] seen = new boolean[n + 1];

for (int num : nums) {
if (num > 0 && num <= n) {
seen[num] = true;
}
}

for (int i = 1; i <= n; i++) {
if (!seen[i]) {
return i;
}
}

return n + 1;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
#hard
Задача: 42. Trapping Rain Water

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

Пример:
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6


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

1️⃣Найдите максимальную высоту столбца с левого конца до индекса i в массиве left_max.

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

3️⃣Итерируйте по массиву высот height и обновляйте ans: добавьте min(left_max[i], right_max[i]) - height[i] к ans.

😎 Решение:
class Solution {
public int trap(int[] height) {
if (height.length == 0) return 0;
int ans = 0;
int size = height.length;
int[] left_max = new int[size];
int[] right_max = new int[size];
left_max[0] = height[0];
for (int i = 1; i < size; i++) {
left_max[i] = Math.max(height[i], left_max[i - 1]);
}
right_max[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--) {
right_max[i] = Math.max(height[i], right_max[i + 1]);
}
for (int i = 1; i < size - 1; i++) {
ans += Math.min(left_max[i], right_max[i]) - height[i];
}
return ans;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#hard
Задача: 43. Multiply Strings

Даны два неотрицательных целых числа num1 и num2, представленные в виде строк. Верните произведение num1 и num2, также представленное в виде строки.

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

Пример:
Input: num1 = "2", num2 = "3"
Output: "6"


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

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

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

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

😎 Решение:
class Solution {
private ArrayList<Integer> addStrings(
ArrayList<Integer> num1,
ArrayList<Integer> num2
) {
ArrayList<Integer> ans = new ArrayList<>();
int carry = 0;

for (int i = 0; i < num1.size() || i < num2.size(); ++i) {
int digit1 = i < num1.size() ? num1.get(i) : 0;
int digit2 = i < num2.size() ? num2.get(i) : 0;
int sum = digit1 + digit2 + carry;
carry = sum / 10;
ans.add(sum % 10);
}

if (carry != 0) {
ans.add(carry);
}
return ans;
}

ArrayList<Integer> multiplyOneDigit(
StringBuilder firstNumber,
char secondNumberDigit,
int numZeros
) {
ArrayList<Integer> currentResult = new ArrayList<>();
for (int i = 0; i < numZeros; ++i) {
currentResult.add(0);
}

int carry = 0;

for (int i = 0; i < firstNumber.length(); ++i) {
char firstNumberDigit = firstNumber.charAt(i);
int multiplication =
(secondNumberDigit - '0') * (firstNumberDigit - '0') + carry;
carry = multiplication / 10;
currentResult.add(multiplication % 10);
}

if (carry != 0) {
currentResult.add(carry);
}
return currentResult;
}

public String multiply(String num1, String num2) {
if (num1.equals("0") || num2.equals("0")) {
return "0";
}

StringBuilder firstNumber = new StringBuilder(num1);
StringBuilder secondNumber = new StringBuilder(num2);

firstNumber.reverse();
secondNumber.reverse();

int N = firstNumber.length() + secondNumber.length();
ArrayList<Integer> ans = new ArrayList<>();
for (int i = 0; i < N; ++i) {
ans.add(0);
}

for (int i = 0; i < secondNumber.length(); ++i) {
ans = addStrings(
multiplyOneDigit(firstNumber, secondNumber.charAt(i), i),
ans
);
}

if (ans.get(ans.size() - 1) == 0) {
ans.remove(ans.size() - 1);
}

StringBuilder answer = new StringBuilder();

for (int i = ans.size() - 1; i >= 0; --i) {
answer.append(ans.get(i));
}

return answer.toString();
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 54. Spiral Matrix

Для матрицы размером m на n верните все элементы матрицы в спиральном порядке.

Пример:
Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [1,2,3,6,9,8,7,4,5]


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

1️⃣Инициализация границ:**
- Инициализируйте верхнюю, правую, нижнюю и левую границы как up, right, down и left.
- Инициализируйте выходной массив result.

2️⃣Проход по элементам в спиральном порядке и добавление каждого элемента в result:**
- Проходите от левой границы к правой границе.
- Проходите от верхней границы к нижней границе.

3️⃣Условия для изменения направления движения:**
- Прежде чем проходить справа налево, убедитесь, что вы не находитесь на строке, которая уже была пройдена. Если это не так, то можно выполнить проход справа налево.
- Аналогично, перед тем как проходить снизу вверх, убедитесь, что вы не находитесь в столбце, который уже был пройден. Затем можно выполнить проход снизу вверх.

4️⃣Обновление границ и возврат результата:**
- Не забудьте переместить границы, обновив left, right, up и down соответственно.
- Верните result.

😎 Решение:
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> result = new ArrayList<>();
int rows = matrix.length;
int columns = matrix[0].length;
int up = 0;
int left = 0;
int right = columns - 1;
int down = rows - 1;

while (result.size() < rows * columns) {
for (int col = left; col <= right; col++) {
result.add(matrix[up][col]);
}
for (int row = up + 1; row <= down; row++) {
result.add(matrix[row][right]);
}
if (up != down) {
for (int col = right - 1; col >= left; col--) {
result.add(matrix[down][col]);
}
}
if (left != right) {
for (int row = down - 1; row > up; row--) {
result.add(matrix[row][left]);
}
}
left++;
right--;
up++;
down--;
}

return result;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
#medium
Задача: 55. Jump Game

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

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

Пример:
Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.


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

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

2️⃣Модификация алгоритма обратного трассирования:
Измените алгоритм обратного трассирования таким образом, чтобы на рекурсивном шаге сначала проверялось, известен ли индекс (GOOD/BAD).
Если индекс известен, тогда возвращается True/False.

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

😎 Решение:
enum Index {
GOOD,
BAD,
UNKNOWN,
}

public class Solution {
Index[] memo;

public boolean canJumpFromPosition(int position, int[] nums) {
if (memo[position] != Index.UNKNOWN) {
return memo[position] == Index.GOOD;
}

int furthestJump = Math.min(position + nums[position], nums.length - 1);
for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
if (canJumpFromPosition(nextPosition, nums)) {
memo[position] = Index.GOOD;
return true;
}
}

memo[position] = Index.BAD;
return false;
}

public boolean canJump(int[] nums) {
memo = new Index[nums.length];
for (int i = 0; i < memo.length; i++) {
memo[i] = Index.UNKNOWN;
}
memo[memo.length - 1] = Index.GOOD;
return canJumpFromPosition(0, nums);
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#medium
Задача: 56. Merge Intervals

Дан массив интервалов, где intervals[i] = [starti, endi]. Объедините все перекрывающиеся интервалы и верните массив неперекрывающихся интервалов, которые покрывают все интервалы во входных данных.

Пример:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6].


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

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

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

3️⃣Объединение интервалов внутри компонент:
Наконец, мы рассматриваем каждую связную компоненту, объединяя все её интервалы, создавая новый интервал с началом, равным минимальному началу среди всех интервалов в компоненте, и концом, равным максимальному концу.

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

class Solution {
private Map<int[], List<int[]>> graph;
private Map<Integer, List<int[]>> nodesInComp;
private Set<int[]> visited;

private boolean overlap(int[] a, int[] b) {
return a[0] <= b[1] && b[0] <= a[1];
}

private void buildGraph(int[][] intervals) {
graph = new HashMap<>();
for (int[] interval : intervals) {
graph.put(interval, new LinkedList<>());
}

for (int[] interval1 : intervals) {
for (int[] interval2 : intervals) {
if (overlap(interval1, interval2)) {
graph.get(interval1).add(interval2);
graph.get(interval2).add(interval1);
}
}
}
}

private int[] mergeNodes(List<int[]> nodes) {
int minStart = nodes.get(0)[0];
for (int[] node : nodes) {
minStart = Math.min(minStart, node[0]);
}

int maxEnd = nodes.get(0)[1];
for (int[] node : nodes) {
maxEnd = Math.max(maxEnd, node[1]);
}

return new int[] { minStart, maxEnd };
}

private void markComponentDFS(int[] start, int compNumber) {
Stack<int[]> stack = new Stack<>();
stack.add(start);

while (!stack.isEmpty()) {
int[] node = stack.pop();
if (!visited.contains(node)) {
visited.add(node);

if (nodesInComp.get(compNumber) == null) {
nodesInComp.put(compNumber, new LinkedList<>());
}
nodesInComp.get(compNumber).add(node);

for (int[] child : graph.get(node)) {
stack.add(child);
}
}
}
}

private void buildComponents(int[][] intervals) {
nodesInComp = new HashMap<>();
visited = new HashSet<>();
int compNumber = 0;

for (int[] interval : intervals) {
if (!visited.contains(interval)) {
markComponentDFS(interval, compNumber);
compNumber++;
}
}
}

public int[][] merge(int[][] intervals) {
buildGraph(intervals);
buildComponents(intervals);

List<int[]> merged = new LinkedList<>();
for (int comp = 0; comp < nodesInComp.size(); comp++) {
merged.add(mergeNodes(nodesInComp.get(comp)));
}

return merged.toArray(new int[merged.size()][]);
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#medium
Задача: 57. Insert Interval

Вам дан массив непересекающихся интервалов intervals, где intervals[i] = [starti, endi] представляет начало и конец i-го интервала, и массив intervals отсортирован в порядке возрастания по starti. Вам также дан интервал newInterval = [start, end], представляющий начало и конец другого интервала.

Вставьте newInterval в массив intervals так, чтобы intervals оставался отсортированным в порядке возрастания по starti и в intervals не было бы перекрывающихся интервалов (при необходимости объедините перекрывающиеся интервалы).

Верните массив intervals после вставки.

Обратите внимание, что не обязательно модифицировать массив intervals на месте. Вы можете создать новый массив и вернуть его.

Пример:
Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]


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

1️⃣ Инициализация переменных:
Инициализируются переменные n и i для хранения размера массива интервалов и текущего индекса соответственно, а также пустой массив res для хранения результата.

2️⃣Обработка случаев без перекрытия и с перекрытием:
В случае отсутствия перекрытия до вставки, проходим через массив интервалов до тех пор, пока конечная точка текущего интервала меньше начальной точки нового интервала. Добавляем текущий интервал в массив res и переходим к следующему.
В случае перекрытия, продолжаем обход, пока начальная точка нового интервала меньше или равна конечной точке текущего интервала. Обновляем начальные и конечные точки нового интервала, объединяя перекрывающиеся интервалы в один.

3️⃣Обработка интервалов после вставки:
Проходим через оставшиеся интервалы после индекса i и добавляем их в массив res. Это включает интервалы, которые следуют после нового интервала и не перекрываются с ним.
Возвращаем массив res, содержащий все интервалы с корректно вставленным новым интервалом.

😎 Решение:
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
int n = intervals.length, i = 0;
List<int[]> res = new ArrayList<>();

while (i < n && intervals[i][1] < newInterval[0]) {
res.add(intervals[i]);
i++;
}

while (i < n && newInterval[1] >= intervals[i][0]) {
newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
i++;
}
res.add(newInterval);

while (i < n) {
res.add(intervals[i]);
i++;
}

return res.toArray(new int[res.size()][]);
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#easy
Задача: 58. Length of Last Word

Дана строка s, состоящая из слов и пробелов. Верните длину последнего слова в строке.

Слово — это максимальная подстрока, состоящая только из символов, не являющихся пробелами.

Пример:
Input: s = "Hello World"
Output: 5
Explanation: The last word is "World" with length 5.


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

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

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

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

😎 Решение:
class Solution {
public int lengthOfLastWord(String s) {
int p = s.length() - 1;
while (p >= 0 && s.charAt(p) == ' ') {
p--;
}

int length = 0;
while (p >= 0 && s.charAt(p) != ' ') {
p--;
length++;
}
return length;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14
#medium
Задача: 59. Spiral Matrix II

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

Пример:
Input: n = 3
Output: [[1,2,3],[8,9,4],[7,6,5]]


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

1️⃣Определение направлений движения:
Для обхода матрицы используются четыре направления, формирующие слои. Создается массив dir, который хранит изменения координат x и y для каждого направления. Например, при движении слева направо (направление №1) координата x остается неизменной, а y увеличивается (x=0, y=1). При движении справа налево (направление №3) x остается неизменным, а y уменьшается (x=0, y=-1).

2️⃣Перемещение по матрице:
Переменные row и col представляют текущие координаты x и y соответственно. Они обновляются в зависимости от направления движения. Применяется предварительно определенный массив dir с изменениями координат x и y для каждого из четырех направлений.

3️⃣Изменение направления:
Направление изменяется, когда следующая строка или столбец в определенном направлении имеют ненулевое значение, что указывает на то, что они уже были пройдены. Переменная d представляет текущий индекс направления. Переход к следующему направлению в массиве dir осуществляется с использованием формулы (d+1)%4. Это позволяет вернуться к направлению 1 после завершения одного полного круга от направления 1 до направления 4.

😎 Решение:
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int cnt = 1;
int dir[][] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
int d = 0;
int row = 0;
int col = 0;
while (cnt <= n * n) {
result[row][col] = cnt++;
int r = Math.floorMod(row + dir[d][0], n);
int c = Math.floorMod(col + dir[d][1], n);

if (result[r][c] != 0) d = (d + 1) % 4;

row += dir[d][0];
col += dir[d][1];
}
return result;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#hard
Задача: 60. Permutation Sequence

Множество [1, 2, 3, ..., n] содержит в общей сложности n! уникальных перестановок.

Списком и маркировкой всех перестановок по порядку, мы получаем следующую последовательность для n = 3:

"123"
"132"
"213"
"231"
"312"
"321"
Дано n и k, верните k-ю перестановку последовательности.

Пример:
Input: n = 3, k = 3
Output: "213"


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

1️⃣Сгенерируйте входной массив nums чисел от 1 до N.

2️⃣Вычислите все факториальные основы от 0 до (N−1)!.

3️⃣Уменьшите k на 1, чтобы значение попало в интервал (0, N!−1).

Используйте коэффициенты факториалов для построения перестановки.

Верните строку перестановки.

😎 Решение:
class Solution {
public String getPermutation(int n, int k) {
int[] factorials = new int[n];
List<Integer> nums = new ArrayList() {{
add(1);
}};

factorials[0] = 1;
for (int i = 1; i < n; ++i) {
factorials[i] = factorials[i - 1] * i;
nums.add(i + 1);
}

--k;

StringBuilder sb = new StringBuilder();
for (int i = n - 1; i > -1; --i) {
int idx = k / factorials[i];
k -= idx * factorials[i];

sb.append(nums.get(idx));
nums.remove(idx);
}
return sb.toString();
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#medium
Задача: 61. Rotate List

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

Пример:
Input: head = [0,1,2], k = 4
Output: [2,0,1]


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

1️⃣Найдите старый хвост и соедините его с головой (old_tail.next = head), чтобы замкнуть кольцо. Одновременно вычислите длину списка n.

2️⃣Найдите новый хвост, который находится на позиции (n - k % n - 1) от головы, и новую голову, которая находится на позиции (n - k % n).

3️⃣Разорвите кольцо (new_tail.next = None) и верните new_head.

😎 Решение:
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null) return null;
if (head.next == null) return head;

ListNode old_tail = head;
int n;
for (n = 1; old_tail.next != null; n++) old_tail = old_tail.next;
old_tail.next = head;

ListNode new_tail = head;
for (int i = 0; i < n - (k % n) - 1; i++) new_tail = new_tail.next;
ListNode new_head = new_tail.next;

new_tail.next = null;

return new_head;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#medium
Задача: 62. Unique Paths

На сетке размером m на n находится робот. Изначально робот расположен в верхнем левом углу (то есть, в клетке grid[0][0]). Робот пытается добраться до нижнего правого угла (то есть, в клетку grid[m - 1][n - 1]). Робот может двигаться только вниз или вправо в любой момент времени.

Даны два целых числа m и n, верните количество возможных уникальных путей, которые робот может пройти, чтобы достичь нижнего правого угла.

Тестовые случаи сгенерированы таким образом, что ответ будет меньше или равен 2 * 10^9.

Пример:
Input: m = 3, n = 7
Output: 28


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

1️⃣Инициализировать двумерный массив d[m][n] = количество путей. Сначала установить количество путей равным 1 для первой строки и первого столбца. Для упрощения можно инициализировать весь двумерный массив единицами.

2️⃣Проитерировать по всем "внутренним" ячейкам: d[col][row] = d[col - 1][row] + d[col][row - 1].

3️⃣Вернуть d[m - 1][n - 1].

😎 Решение:
class Solution {
public int uniquePaths(int m, int n) {
if (m == 1 || n == 1) {
return 1;
}
return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2