Задача: №21. Merge Two Sorted Lists #easy
Условие:
Решение:
Пояснение:
Рассмотрим, как работает предложенное решение для объединения двух отсортированных связанных списков.
Рекурсивное слияние:
Функция mergeTwoLists рекурсивно сливает два списка, list1 и list2.
Базовый случай:
Если list1 равен null, это означает, что первый список пуст, и нам просто нужно вернуть list2.
Аналогично, если list2 равен null, это означает, что второй список пуст, и нам нужно вернуть list1.
Сравнение значений узлов:
Если оба списка не пусты, мы сравниваем значения текущих узлов list1 и list2.
Если значение текущего узла list1 меньше значения текущего узла list2, мы рекурсивно вызываем mergeTwoLists для следующего узла list1 и текущего узла list2, и устанавливаем следующий узел для list1. Затем возвращаем list1.
Иначе, мы рекурсивно вызываем mergeTwoLists для текущего узла list1 и следующего узла list2, и устанавливаем следующий узел для list2. Затем возвращаем list2.
Соединение списков:
Процесс повторяется до тех пор, пока не будут обработаны все узлы обоих списков. В итоге, мы получаем отсортированный объединенный список.
Временная и пространственная сложность:
Временная сложность: O(m + n), где m и n — длины списков list1 и list2 соответственно. Мы проходим по каждому узлу обоих списков один раз.
Пространственная сложность: O(m + n) из-за рекурсивных вызовов, которые могут занимать стековую память, пропорциональную сумме длин обоих списков.
Условие:
Вам даны заголовки двух отсортированных связанных списков list1 и list2. Объедините два списка в один отсортированный список. Список должен быть составлен путем сращивания узлов первых двух списков. Возвращает заголовок объединенного связанного списка.
Решение:
/**
* 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;
}
}
if(list1==null)
return list2;
return list1;
}
}
Пояснение:
Рассмотрим, как работает предложенное решение для объединения двух отсортированных связанных списков.
Рекурсивное слияние:
Функция mergeTwoLists рекурсивно сливает два списка, list1 и list2.
Базовый случай:
Если list1 равен null, это означает, что первый список пуст, и нам просто нужно вернуть list2.
Аналогично, если list2 равен null, это означает, что второй список пуст, и нам нужно вернуть list1.
Сравнение значений узлов:
Если оба списка не пусты, мы сравниваем значения текущих узлов list1 и list2.
Если значение текущего узла list1 меньше значения текущего узла list2, мы рекурсивно вызываем mergeTwoLists для следующего узла list1 и текущего узла list2, и устанавливаем следующий узел для list1. Затем возвращаем list1.
Иначе, мы рекурсивно вызываем mergeTwoLists для текущего узла list1 и следующего узла list2, и устанавливаем следующий узел для list2. Затем возвращаем list2.
Соединение списков:
Процесс повторяется до тех пор, пока не будут обработаны все узлы обоих списков. В итоге, мы получаем отсортированный объединенный список.
Временная и пространственная сложность:
Временная сложность: O(m + n), где m и n — длины списков list1 и list2 соответственно. Мы проходим по каждому узлу обоих списков один раз.
Пространственная сложность: O(m + n) из-за рекурсивных вызовов, которые могут занимать стековую память, пропорциональную сумме длин обоих списков.
👍7💊2❤1
Задача: 22. Generate Parentheses #medium
Условие:
Решение:
Пояснение:
1. Метод `generateParenthesis(int n)`:
- Создание множества HashSet ans для хранения уникальных комбинаций скобок.
- Вызов метода createParenthesis(n, ans), который будет заполнять множество ans всеми возможными комбинациями скобок.
- Вывод результата в консоль.
- Возврат списка (ArrayList) комбинаций скобок из множества ans.
2. Метод `createParenthesis(int n, Set<String> currentList)`:
- Создание временного множества temp для хранения временных комбинаций скобок.
- Проверка, если n <= 0, возвращается пустое множество.
- Если n == 1, добавляется базовая комбинация "()" во временное множество temp.
- Рекурсивный вызов createParenthesis(n - 1, currentList) для генерации комбинаций скобок с меньшим количеством пар.
- Для каждой комбинации в currentList:
- Перебор индексов в комбинации.
- Формирование новой комбинации путем вставки "()" в каждую позицию.
- Добавление полученной комбинации во временное множество temp.
- Обновление currentList на основе временного множества temp.
- Возврат текущего списка комбинаций.
Этот код позволяет генерировать и возвращать все возможные валидные комбинации скобок для заданного количества пар скобок n. Рекурсивный подход позволяет итеративно строить комбинации, добавляя "()" в различные позиции.
Условие:
Учитывая n пар круглых скобок, напишите функцию, которая генерирует все комбинации правильных круглых скобок.
Решение:
public List<String> generateParenthesis(int n) {
Set<String> ans = new HashSet<>();
ans = createParenthesis(n, ans);
System.out.println("Ans: " + ans);
return new ArrayList<>(ans);
}
private static Set<String> createParenthesis(int n, Set<String> currentList) {
java.util.Set<String> temp = new HashSet<>();
if (n <= 0) return new HashSet<>();
if (n == 1) {
temp.add("()");
return temp;
}
currentList = createParenthesis(n - 1, currentList);
System.out.println("At n-1 " + String.valueOf(n-1) + ": " + currentList);
for (String parenthesis : currentList) {
for (int i = 0; i <= parenthesis.length(); i++) {
String newStr;
if (i == 0) {
System.out.println("At 0 for " + parenthesis);
newStr = "()" + parenthesis;
System.out.println(newStr);
} else if (i == parenthesis.length()) {
System.out.println("At last for " + parenthesis);
newStr = parenthesis + "()";
System.out.println(newStr);
} else {
System.out.println("At " + i + " for " + parenthesis);
newStr = parenthesis.substring(0, i) + "()" + parenthesis.substring(i);
System.out.println(newStr);
}
temp.add(newStr);
}
}
currentList = temp;
return currentList;
}Пояснение:
1. Метод `generateParenthesis(int n)`:
- Создание множества HashSet ans для хранения уникальных комбинаций скобок.
- Вызов метода createParenthesis(n, ans), который будет заполнять множество ans всеми возможными комбинациями скобок.
- Вывод результата в консоль.
- Возврат списка (ArrayList) комбинаций скобок из множества ans.
2. Метод `createParenthesis(int n, Set<String> currentList)`:
- Создание временного множества temp для хранения временных комбинаций скобок.
- Проверка, если n <= 0, возвращается пустое множество.
- Если n == 1, добавляется базовая комбинация "()" во временное множество temp.
- Рекурсивный вызов createParenthesis(n - 1, currentList) для генерации комбинаций скобок с меньшим количеством пар.
- Для каждой комбинации в currentList:
- Перебор индексов в комбинации.
- Формирование новой комбинации путем вставки "()" в каждую позицию.
- Добавление полученной комбинации во временное множество temp.
- Обновление currentList на основе временного множества temp.
- Возврат текущего списка комбинаций.
Этот код позволяет генерировать и возвращать все возможные валидные комбинации скобок для заданного количества пар скобок n. Рекурсивный подход позволяет итеративно строить комбинации, добавляя "()" в различные позиции.
👍6
Задача: 23. Merge k Sorted Lists #hard
Условие:
Решение:
Пояснение:
Этот код представляет собой решение задачи слияния k отсортированных связанных списков в один отсортированный связанный список.
Подход к решению задачи заключается в использовании приоритетной очереди (PriorityQueue) для хранения узлов всех списков. При этом элементы будут храниться в очереди с учетом их значений, чтобы мы могли получать самый минимальный узел каждый раз.
Мы начинаем с создания minHeap, который представляет собой приоритетную очередь, отсортированную по значениям узлов. Мы добавляем все узлы начальных списков в эту очередь.
Затем мы начинаем извлекать и обрабатывать узлы из очереди. Мы извлекаем минимальный узел из очереди и добавляем его значение в новый связанный список. Затем мы проверяем, есть ли следующий узел этого списка, и если есть, добавляем его в очередь.
Мы продолжаем этот процесс до тех пор, пока очередь не опустеет. В конце, когда все узлы из очереди обработаны, мы возвращаем голову нового слияния отсортированного списка.
Таким образом, данный код эффективно объединяет k отсортированных списков в один отсортированный список, используя приоритетную очередь для поддержания правильного порядка узлов.
Условие:
Вам дан массив из k списков связанных списков, каждый связанный список отсортирован в порядке возрастания.
Объедините все связанные списки в один отсортированный связанный список и верните его.
Решение:
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length==0) return null;
Queue<ListNode> minHeap = new PriorityQueue<>((head1,head2)->head1.val-head2.val);
for(ListNode listNode: lists){
if(listNode!=null) minHeap.offer(listNode);
}
ListNode head=null,cur=null;
while(!minHeap.isEmpty()){
ListNode curNode = minHeap.poll();
if(curNode.next!=null) minHeap.offer(curNode.next);
if(head==null){
head = new ListNode(curNode.val);
cur = head;
}
else{
cur.next = new ListNode(curNode.val);
cur = cur.next;
}
}
return head;
}
}Пояснение:
Этот код представляет собой решение задачи слияния k отсортированных связанных списков в один отсортированный связанный список.
Подход к решению задачи заключается в использовании приоритетной очереди (PriorityQueue) для хранения узлов всех списков. При этом элементы будут храниться в очереди с учетом их значений, чтобы мы могли получать самый минимальный узел каждый раз.
Мы начинаем с создания minHeap, который представляет собой приоритетную очередь, отсортированную по значениям узлов. Мы добавляем все узлы начальных списков в эту очередь.
Затем мы начинаем извлекать и обрабатывать узлы из очереди. Мы извлекаем минимальный узел из очереди и добавляем его значение в новый связанный список. Затем мы проверяем, есть ли следующий узел этого списка, и если есть, добавляем его в очередь.
Мы продолжаем этот процесс до тех пор, пока очередь не опустеет. В конце, когда все узлы из очереди обработаны, мы возвращаем голову нового слияния отсортированного списка.
Таким образом, данный код эффективно объединяет k отсортированных списков в один отсортированный список, используя приоритетную очередь для поддержания правильного порядка узлов.
❤3👍3
Задача: 24. Swap Nodes in Pairs #medium
Условие:
Решение:
Пояснение:
Этот код реализует функцию swapPairs, которая меняет местами каждую пару соседних узлов в связанном списке. Если в списке нечетное количество узлов, последний узел остается на своем месте.
Функция начинает с проверки базовых случаев: если список пуст или содержит только один узел, то возвращается исходный список, так как в нем нет пар для обмена.
Затем функция продолжает рекурсивно вызывать саму себя для оставшейся части списка (начиная со второго узла) и последующих узлов, после того как пара была обменена.
Далее мы меняем местами текущую пару узлов: второй узел становится головой, а первый узел становится его следующим узлом. Затем мы связываем второй узел со следующей после него парой, которая возвращается из рекурсивного вызова.
Наконец, после прохода по всем парам узлов, функция возвращает новую голову списка, которая была получена после всех обменов.
Таким образом, данный код эффективно меняет местами каждую пару соседних узлов в связанном списке, используя рекурсию для обработки всех пар узлов.
Условие:
Учитывая связанный список, поменяйте местами каждые два соседних узла и верните его голову. Вы должны решить проблему, не изменяя значения в узлах списка (т. е. изменять можно только сами узлы).
Решение:
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next==null) return head;
ListNode second = head.next;
ListNode pr = swapPairs(second.next);
second.next = head;
head.next = pr;
return second;
}
}Пояснение:
Этот код реализует функцию swapPairs, которая меняет местами каждую пару соседних узлов в связанном списке. Если в списке нечетное количество узлов, последний узел остается на своем месте.
Функция начинает с проверки базовых случаев: если список пуст или содержит только один узел, то возвращается исходный список, так как в нем нет пар для обмена.
Затем функция продолжает рекурсивно вызывать саму себя для оставшейся части списка (начиная со второго узла) и последующих узлов, после того как пара была обменена.
Далее мы меняем местами текущую пару узлов: второй узел становится головой, а первый узел становится его следующим узлом. Затем мы связываем второй узел со следующей после него парой, которая возвращается из рекурсивного вызова.
Наконец, после прохода по всем парам узлов, функция возвращает новую голову списка, которая была получена после всех обменов.
Таким образом, данный код эффективно меняет местами каждую пару соседних узлов в связанном списке, используя рекурсию для обработки всех пар узлов.
👍3❤1
Задача: 25. Reverse Nodes in k-Group #hard
Условие:
Решение:
Пояснение:
Данный код реализует функцию reverseKGroup, которая разворачивает узлы связанного списка попарно с шагом k, начиная с головы списка. Если в конце списка остается менее чем k элементов, то они остаются на своих местах.
В начале функции создается фиктивный узел dummy, который служит вспомогательным узлом перед началом исходного списка. Указатель pointer устанавливается на dummy для начала обхода списка.
Далее функция начинает цикл, который продолжается до тех пор, пока указатель pointer не достигнет конца списка. Внутри цикла устанавливается node на текущий узел, после чего происходит обход k элементов или до конца списка. Если достигнут конец списка, то цикл прерывается.
Затем происходит разворот k узлов. Для каждой пары узлов происходит их обмен местами. Затем устанавливается связь между предыдущим и следующим блоками узлов.
Наконец, после всех обменов и перестановок блоков узлов, функция возвращает новую голову списка (которая была установлена в начале), пропуская фиктивный узел dummy.
Таким образом, данный код эффективно разворачивает узлы связанного списка попарно с шагом k, используя цикл для обхода списка и особые операции для обмена и соединения узлов в блоках.
Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка k за раз и верните измененный список.
k — целое положительное число, меньшее или равное длине связанного списка. Если количество узлов не кратно k, то пропущенные узлы в конечном итоге должны остаться такими, какие они есть.
Вы не можете изменять значения в узлах списка, можно изменять только сами узлы.
Решение:
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pointer = dummy;
while (pointer != null) {
ListNode node = pointer;
for (int i = 0; i < k && node != null; i++) node = node.next;
if (node == null) break;
// reverse the k nodes
ListNode prev = null, curr = pointer.next;
for (int i = 0; i < k; i++) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
ListNode tail = pointer.next;
tail.next = curr;
pointer.next = prev;
pointer = tail;
}
return dummy.next;
}Пояснение:
Данный код реализует функцию reverseKGroup, которая разворачивает узлы связанного списка попарно с шагом k, начиная с головы списка. Если в конце списка остается менее чем k элементов, то они остаются на своих местах.
В начале функции создается фиктивный узел dummy, который служит вспомогательным узлом перед началом исходного списка. Указатель pointer устанавливается на dummy для начала обхода списка.
Далее функция начинает цикл, который продолжается до тех пор, пока указатель pointer не достигнет конца списка. Внутри цикла устанавливается node на текущий узел, после чего происходит обход k элементов или до конца списка. Если достигнут конец списка, то цикл прерывается.
Затем происходит разворот k узлов. Для каждой пары узлов происходит их обмен местами. Затем устанавливается связь между предыдущим и следующим блоками узлов.
Наконец, после всех обменов и перестановок блоков узлов, функция возвращает новую голову списка (которая была установлена в начале), пропуская фиктивный узел dummy.
Таким образом, данный код эффективно разворачивает узлы связанного списка попарно с шагом k, используя цикл для обхода списка и особые операции для обмена и соединения узлов в блоках.
🔥1
Задача: 26. Remove Duplicates from Sorted Array #easy
Условие:
Решение:
Пояснение:
Данный код представляет собой решение задачи удаления дубликатов из отсортированного массива nums. Подход к решению задачи основан на использовании двух указателей - left и right, которые будут указывать на элементы массива для обработки.
Изначально устанавливаются указатели left и right на первый и второй элементы массива соответственно. Далее происходит итерация по массиву от второго элемента до последнего. Если значение элемента right равно значению элемента left, то просто увеличиваем right, чтобы пропустить дубликат. В противном случае мы перемещаем уникальный элемент, указанный right, на позицию после уникального элемента, указанного left, и помещаем его на эту позицию. Затем увеличиваем left и right для продолжения обработки.
В конце функция возвращает left + 1, что представляет собой индекс последнего уникального элемента в массиве, увеличенный на 1 для получения общего количества уникальных элементов в массиве.
Таким образом, данный код эффективно удаляет дубликаты из отсортированного массива, работая с двумя указателями и перемещая уникальные элементы на правильные позиции.
Условие:
Учитывая целочисленный массив чисел, отсортированный в неубывающем порядке, удалите дубликаты на месте так, чтобы каждый уникальный элемент появлялся только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в числах.
Считайте, что количество уникальных элементов чисел равно k. Чтобы вас приняли, вам нужно сделать следующее:
Измените массив nums так, чтобы первые k элементов nums содержали уникальные элементы в том порядке, в котором они присутствовали в nums изначально. Остальные элементы nums не важны, как и размер nums.
Вернуть К.
Решение:
class Solution {
public int removeDuplicates(int[] nums) {
int n = nums.length;
if(n<=1){return n;}
int left = 0; //everything to the left of left(inclusive) is good
int right = 1;//the current element we are processing
while(right<=n-1){
if(nums[right] == nums[left]){
right++;
}
else{
nums[left+1] = nums[right];
left++;
right++;
}
}
return left+1;
}
}Пояснение:
Данный код представляет собой решение задачи удаления дубликатов из отсортированного массива nums. Подход к решению задачи основан на использовании двух указателей - left и right, которые будут указывать на элементы массива для обработки.
Изначально устанавливаются указатели left и right на первый и второй элементы массива соответственно. Далее происходит итерация по массиву от второго элемента до последнего. Если значение элемента right равно значению элемента left, то просто увеличиваем right, чтобы пропустить дубликат. В противном случае мы перемещаем уникальный элемент, указанный right, на позицию после уникального элемента, указанного left, и помещаем его на эту позицию. Затем увеличиваем left и right для продолжения обработки.
В конце функция возвращает left + 1, что представляет собой индекс последнего уникального элемента в массиве, увеличенный на 1 для получения общего количества уникальных элементов в массиве.
Таким образом, данный код эффективно удаляет дубликаты из отсортированного массива, работая с двумя указателями и перемещая уникальные элементы на правильные позиции.
👍12❤2
Задача: 27. Remove Element #easy
Условие:
Решение:
Пояснение:
Данный код решает задачу удаления всех вхождений указанного значения val из массива nums. Подход к решению основан на использовании переменной left_most_index, которая указывает на индекс, куда должны быть помещены уникальные элементы массива.
В начале работы функции removeElement переменная left_most_index инициализируется значением 0. Затем происходит итерация по массиву nums, где для каждого элемента проверяется, не равен ли он значению val. Если элемент отличается от val, он считается уникальным и помещается на позицию с индексом left_most_index, после чего значение left_most_index увеличивается на 1.
По завершении итерации left_most_index содержит количество уникальных элементов, находящихся в массиве после удаления всех вхождений val. Функция возвращает left_most_index, который является размером нового массива, содержащего только уникальные элементы.
Таким образом, данный код эффективно удаляет все вхождения указанного значения из массива, перемещая уникальные элементы в начало массива и возвращая общее количество уникальных элементов.
Условие:
Учитывая целочисленный массив nums и целочисленное значение, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в виде чисел, которые не равны val.
Учитывайте количество элементов в nums, которые не равны val be k. Чтобы вас приняли, вам необходимо сделать следующее:
Измените массив nums так, чтобы первые k элементов nums содержали элементы, не равные val. Остальные элементы nums не важны, как и размер nums.
Вернуть К.
Решение:
class Solution {
public int removeElement(int[] nums, int val) {
int left_most_index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) {
nums[left_most_index++] = nums[i];
}
}
return left_most_index;
}
}Пояснение:
Данный код решает задачу удаления всех вхождений указанного значения val из массива nums. Подход к решению основан на использовании переменной left_most_index, которая указывает на индекс, куда должны быть помещены уникальные элементы массива.
В начале работы функции removeElement переменная left_most_index инициализируется значением 0. Затем происходит итерация по массиву nums, где для каждого элемента проверяется, не равен ли он значению val. Если элемент отличается от val, он считается уникальным и помещается на позицию с индексом left_most_index, после чего значение left_most_index увеличивается на 1.
По завершении итерации left_most_index содержит количество уникальных элементов, находящихся в массиве после удаления всех вхождений val. Функция возвращает left_most_index, который является размером нового массива, содержащего только уникальные элементы.
Таким образом, данный код эффективно удаляет все вхождения указанного значения из массива, перемещая уникальные элементы в начало массива и возвращая общее количество уникальных элементов.
👍2❤1
Задача: 28. Find the Index of the First Occurrence in a String #easy
Условие:
Решение:
Пояснение:
В классе Solution содержатся два метода: failureFunction и strStr. Метод failureFunction является закрытым и используется для построения функции сбоев для заданного символьного массива str. Он возвращает массив целых чисел f, длина которого на 1 больше длины входного массива. Алгоритм метода основан на построении функции сбоев с помощью цикла с условиями проверки и корректировкой значений в массиве f.
Метод strStr является публичным и решает задачу поиска подстроки needle в строке haystack. Если длина подстроки needle равна 0, метод возвращает 0. После проверки условия на длину подстроки, метод вызывает failureFunction для подстроки needle и сохраняет значения функции сбоев в массив f. Затем происходит итерация по строке haystack, сравнение символов с подстрокой needle, и в случае несовпадения использование значений из массива f для правильного сдвига.
Когда метод найдет подстроку needle в строке haystack, он вернет индекс начала найденной подстроки. Если ничего не будет найдено, метод вернет -1. Таким образом, эти методы вместе представляют реализацию алгоритма поиска подстроки с использованием функции сбоев, основанной на алгоритме Кнута-Морриса-Пратта.
Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -1, если игла не является частью стога сена.
Решение:
public class Solution {
private int[] failureFunction(char[] str) {
int[] f = new int[str.length+1];
for (int i = 2; i < f.length; i++) {
int j = f[i-1];
while (j > 0 && str[j] != str[i-1]) j = f[j];
if (j > 0 || str[j] == str[i-1]) f[i] = j+1;
}
return f;
}
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
if (needle.length() <= haystack.length()) {
int[] f = failureFunction(needle.toCharArray());
int i = 0, j = 0;
while (i < haystack.length()) {
if (haystack.charAt(i) == needle.charAt(j)) {
i++; j++;
if (j == needle.length()) return i-j;
} else if (j > 0) j = f[j];
else i++;
}
}
return -1;
}
}Пояснение:
В классе Solution содержатся два метода: failureFunction и strStr. Метод failureFunction является закрытым и используется для построения функции сбоев для заданного символьного массива str. Он возвращает массив целых чисел f, длина которого на 1 больше длины входного массива. Алгоритм метода основан на построении функции сбоев с помощью цикла с условиями проверки и корректировкой значений в массиве f.
Метод strStr является публичным и решает задачу поиска подстроки needle в строке haystack. Если длина подстроки needle равна 0, метод возвращает 0. После проверки условия на длину подстроки, метод вызывает failureFunction для подстроки needle и сохраняет значения функции сбоев в массив f. Затем происходит итерация по строке haystack, сравнение символов с подстрокой needle, и в случае несовпадения использование значений из массива f для правильного сдвига.
Когда метод найдет подстроку needle в строке haystack, он вернет индекс начала найденной подстроки. Если ничего не будет найдено, метод вернет -1. Таким образом, эти методы вместе представляют реализацию алгоритма поиска подстроки с использованием функции сбоев, основанной на алгоритме Кнута-Морриса-Пратта.
👍1🤔1
Задача: 29. Divide Two Integers #medium
Условие:
Решение:
Пояснение:
Метод divide принимает два целых числа dividend и divisor и возвращает результат их деления. Внутри метода вызывается вспомогательный метод divideLong, который работает с длинными числами типа long. Результат деления сохраняется в переменной result, а затем проверяется на превышение максимального значения типа int. Если результат больше Integer.MAX_VALUE, то возвращается Integer.MAX_VALUE, иначе результат приводится к типу int и возвращается.
Метод divideLong используется для обработки деления на длинных числах, что позволяет более удобно работать с граничными случаями. Сначала определяется знак результата деления путем сравнения знаков делимого и делителя. Затем делимое и делитель приводятся к неотрицательным значениям, чтобы избежать проблем с делением отрицательных чисел.
Если делимое меньше делителя, то возвращается 0. Далее происходит итерация в цикле, в котором значение делителя удваивается до тех пор, пока не будет превышено делимое. При этом также увеличивается счётчик divide. После завершения итераций рекурсивно вызывается divideLong для разности делимого и текущей суммы, и результату добавляется текущее значение счётчика divide. При отрицательном результате деления результат умножается на -1. Таким образом, данный метод реализует деление чисел с использованием итераций и основывается на принципе удвоения значения делителя для нахождения результата.
Условие:
Учитывая два целых числа: делимое и делитель, разделите два целых числа, не используя операторы умножения, деления и модификатора.
Целочисленное деление должно сокращаться до нуля, что означает потерю дробной части. Например, 8,345 будет сокращено до 8, а -2,7335 будет сокращено до -2.
Возвращает частное после деления делимого на делитель.
Примечание. Предположим, мы имеем дело со средой, которая может хранить целые числа только в пределах 32-битного целого диапазона со знаком: [−2^31, 2^31 − 1]. В этой задаче, если частное строго больше 2^31 - 1, верните 2^31 - 1, а если частное строго меньше -2^31, верните -2^31.
Решение:
public int divide(int dividend, int divisor) {
long result = divideLong(dividend, divisor);
return result > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)result;
}
// It's easy to handle edge cases when
// operate with long numbers rather than int
public long divideLong(long dividend, long divisor) {
// Remember the sign
boolean negative = dividend < 0 != divisor < 0;
// Make dividend and divisor unsign
if (dividend < 0) dividend = -dividend;
if (divisor < 0) divisor = -divisor;
// Return if nothing to divide
if (dividend < divisor) return 0;
// Sum divisor 2, 4, 8, 16, 32 .... times
long sum = divisor;
long divide = 1;
while ((sum+sum) <= dividend) {
sum += sum;
divide += divide;
}
// Make a recursive call for (devided-sum) and add it to the result
return negative ? -(divide + divideLong((dividend-sum), divisor)) :
(divide + divideLong((dividend-sum), divisor));
}Пояснение:
Метод divide принимает два целых числа dividend и divisor и возвращает результат их деления. Внутри метода вызывается вспомогательный метод divideLong, который работает с длинными числами типа long. Результат деления сохраняется в переменной result, а затем проверяется на превышение максимального значения типа int. Если результат больше Integer.MAX_VALUE, то возвращается Integer.MAX_VALUE, иначе результат приводится к типу int и возвращается.
Метод divideLong используется для обработки деления на длинных числах, что позволяет более удобно работать с граничными случаями. Сначала определяется знак результата деления путем сравнения знаков делимого и делителя. Затем делимое и делитель приводятся к неотрицательным значениям, чтобы избежать проблем с делением отрицательных чисел.
Если делимое меньше делителя, то возвращается 0. Далее происходит итерация в цикле, в котором значение делителя удваивается до тех пор, пока не будет превышено делимое. При этом также увеличивается счётчик divide. После завершения итераций рекурсивно вызывается divideLong для разности делимого и текущей суммы, и результату добавляется текущее значение счётчика divide. При отрицательном результате деления результат умножается на -1. Таким образом, данный метод реализует деление чисел с использованием итераций и основывается на принципе удвоения значения делителя для нахождения результата.
👍3
Задача: 30. Substring with Concatenation of All Words #hard
Условие:
Решение:
Пояснение:
Класс Solution содержит метод findSubstring, который принимает строку s и массив слов words и возвращает список целых чисел. Алгоритм findSubstring осуществляет поиск всех начальных индексов в строке s, где все слова из массива words встречаются последовательно. Внутри метода используются различные структуры данных, такие как HashMap и массивы для эффективного хранения данных и проверки условий.
На первом этапе метод формирует HashMap с подсчетом количества каждого слова из массива words и вычисляет длины слов и общую длину массива слов. Далее инициализируется массив arr, который используется для подсчета количества символов в каждом слове. Затем происходит последовательное сравнение слов в строке s с массивом words, с учётом совпадения и корректности слов и их количества. Если все условия соблюдаются, то добавляется индекс начала найденной подстроки в список ответа answer.
Основные вспомогательные методы allZeros и validWords используются для проверки, все ли элементы массива равны нулю и соответствуют ли найденные слова ожидаемым словам из массива words. В результате успешного анализа строки метод findSubstring возвращает список индексов начала каждой найденной подстроки.
Условие:
Вам дана строка s и массив строк-слов. Все строки слов имеют одинаковую длину.
Объединенная строка — это строка, которая в точности содержит все строки любой перестановки объединенных слов.
Например, если слова = ["ab", "cd", "ef"], то "abcdef", "abefcd", "cdabef", "cdefab", "efabcd" и "efcdab" являются объединенными строками. «acdbef» не является объединенной строкой, поскольку не является объединением какой-либо перестановки слов.
Возвращает массив начальных индексов всех объединенных подстрок в s. Вы можете вернуть ответ в любом порядке.
Решение:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> answer = new ArrayList<>();
HashMap<String,Integer> mapOfWords = new HashMap<>();
for(String word: words) mapOfWords.put(word,mapOfWords.getOrDefault(word,0)+1);
int k = words[0].length();
int n = words.length*k;
int sLen = s.length();
if(sLen<n) return answer;
int[] arr = new int[26];
for(String word: words){
for(int i=0;i<k;i++){
arr[word.charAt(i)-'a']++;
}
}
int start=0,end=n-1;
for(int i=0;i<=end;i++) arr[s.charAt(i)-'a']--;
while(end<s.length()-1){
if(allZeros(arr)&&validWords(s.substring(start,end+1),new HashMap<>(mapOfWords),k)) answer.add(start);
arr[s.charAt(start)-'a']++;
start++;end++;
arr[s.charAt(end)-'a']--;
}
if(allZeros(arr)&&validWords(s.substring(start,end+1),new HashMap<>(mapOfWords),k)) answer.add(start);
return answer;
}
public boolean allZeros(int[] arr){
return Arrays.stream(arr).allMatch(i->i==0);
}
public boolean validWords(String s, HashMap<String,Integer> mapOfWords, int k){
for(int i=0;i*k<s.length();i++){
String currentString = s.substring(i*k,(i+1)*k);
if(mapOfWords.containsKey(currentString)){
mapOfWords.put(currentString,mapOfWords.get(currentString)-1);
if(mapOfWords.get(currentString)<0) return false;
}
else return false;
}
return true;
}
}Пояснение:
Класс Solution содержит метод findSubstring, который принимает строку s и массив слов words и возвращает список целых чисел. Алгоритм findSubstring осуществляет поиск всех начальных индексов в строке s, где все слова из массива words встречаются последовательно. Внутри метода используются различные структуры данных, такие как HashMap и массивы для эффективного хранения данных и проверки условий.
На первом этапе метод формирует HashMap с подсчетом количества каждого слова из массива words и вычисляет длины слов и общую длину массива слов. Далее инициализируется массив arr, который используется для подсчета количества символов в каждом слове. Затем происходит последовательное сравнение слов в строке s с массивом words, с учётом совпадения и корректности слов и их количества. Если все условия соблюдаются, то добавляется индекс начала найденной подстроки в список ответа answer.
Основные вспомогательные методы allZeros и validWords используются для проверки, все ли элементы массива равны нулю и соответствуют ли найденные слова ожидаемым словам из массива words. В результате успешного анализа строки метод findSubstring возвращает список индексов начала каждой найденной подстроки.
👍3
#medium
Задача: 31. Next Permutation
Перестановка массива целых чисел — это упорядочивание его элементов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.
Для данного массива целых чисел nums найдите следующую перестановку nums.
Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.
Пример:
👨💻 Алгоритм:
1️⃣ Мы меняем местами числа a[i−1] и a[j]. Теперь у нас есть правильное число на индексе i−1. Однако текущая перестановка ещё не является той перестановкой, которую мы ищем. Нам нужна наименьшая перестановка, которая может быть сформирована с использованием чисел только справа от a[i−1]. Следовательно, нам нужно расположить эти числа в порядке возрастания, чтобы получить их наименьшую перестановку.
2️⃣ Однако, вспомните, что, сканируя числа справа налево, мы просто уменьшали индекс, пока не нашли пару a[i] и a[i−1], где a[i] > a[i−1]. Таким образом, все числа справа от a[i−1] уже были отсортированы в порядке убывания. Более того, обмен местами a[i−1] и a[j] не изменил этот порядок.
3️⃣ Поэтому нам просто нужно перевернуть числа, следующие за a[i−1], чтобы получить следующую наименьшую лексикографическую перестановку.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 31. Next Permutation
Перестановка массива целых чисел — это упорядочивание его элементов в последовательность или линейный порядок.
Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).
Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.
Для данного массива целых чисел nums найдите следующую перестановку nums.
Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.
Пример:
Input: nums = [1,2,3]
Output: [1,3,2]
public class Solution {
public void nextPermutation(int[] nums) {
int i = nums.length - 2;
while (i >= 0 && nums[i + 1] <= nums[i]) {
i--;
}
if (i >= 0) {
int j = nums.length - 1;
while (nums[j] <= nums[i]) {
j--;
}
swap(nums, i, j);
}
reverse(nums, i + 1);
}
private void reverse(int[] nums, int start) {
int i = start, j = nums.length - 1;
while (i < j) {
swap(nums, i, j);
i++;
j--;
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
#hard
Задача: 32. Longest Valid Parentheses
Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.
Пример:
👨💻 Алгоритм:
1️⃣ В этом подходе мы рассматриваем каждую возможную непустую подстроку чётной длины из заданной строки и проверяем, является ли она корректной строкой скобок. Для проверки корректности мы используем метод стека.
2️⃣ Каждый раз, когда мы встречаем символ ‘(’, мы кладём его в стек. Для каждого встреченного символа ‘)’ мы извлекаем из стека символ ‘(’. Если символ ‘(’ недоступен в стеке для извлечения в любой момент или если в стеке остались элементы после обработки всей подстроки, подстрока скобок является некорректной.
3️⃣ Таким образом, мы повторяем процесс для каждой возможной подстроки и продолжаем сохранять длину самой длинной найденной корректной строки.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 32. Longest Valid Parentheses
Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.
Пример:
Input: s = "(()"
Output: 2
import java.util.Stack;
public class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(') {
stack.push('(');
} else if (!stack.isEmpty() && stack.peek() == '(') {
stack.pop();
} else {
return false;
}
}
return stack.isEmpty();
}
public int longestValidParentheses(String s) {
int maxlen = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 2; j <= s.length(); j += 2) {
String substring = s.substring(i, j);
if (isValid(substring)) {
maxlen = Math.max(maxlen, j - i);
}
}
}
return maxlen;
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
#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).
Пример:
👨💻 Алгоритм:
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.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 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
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].
В противном случае выполните двоичный поиск по подмассиву 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;
}
}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).
Пример:
👨💻 Алгоритм:
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, чтобы получить последнее вхождение, а затем возвращаем результат.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 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]
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а 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.
Если 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;
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#easy
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
👨💻 Алгоритм:
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.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 35. Search Insert Position
Дан отсортированный массив уникальных целых чисел и целевое значение. Верните индекс, если цель найдена. Если нет, верните индекс, где она должна быть вставлена в соответствии с порядком.
Вы должны написать алгоритм со временной сложностью O(log n).
Пример:
Input: nums = [1,3,5,6], target = 5
Output: 2
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
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;
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
#medium
Задача: 36. Valid Sudoku
Определите, является ли доска Судоку размером 9 на 9 валидной. Необходимо проверить только заполненные ячейки согласно следующим правилам:
Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.
Пример:
👨💻 Алгоритм:
1️⃣ Инициализируйте список из 9 хеш-множеств, где хеш-множество с индексом r будет использоваться для хранения ранее увиденных чисел в строке r судоку. Аналогично инициализируйте списки из 9 хеш-множеств для отслеживания столбцов и блоков.
2️⃣ Итерируйтесь по каждой позиции (r, c) в судоку. На каждой итерации, если на текущей позиции есть число:
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
3️⃣ В противном случае обновите множество, отвечающее за отслеживание ранее увиденных чисел в текущей строке, столбце и блоке. Индекс текущего блока рассчитывается как (r / 3) * 3 + (c / 3), где / означает деление нацело.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 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
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните 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;
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#hard
Задача: 37. Sudoku Solver
Напишите программу для решения головоломки Судоку, заполнив пустые ячейки.
Решение Судоку должно удовлетворять всем следующим правилам:
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.
Пример:
👨💻 Алгоритм:
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).
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 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"]]
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (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 : '.';
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
#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-й элемент последовательности "считай и скажи".
Пример:
👨💻 Алгоритм:
1️⃣ Мы хотим использовать шаблон, который соответствует строкам из одинаковых символов, таких как "4", "7777", "2222222".
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.
2️⃣ Мы можем разбить это регулярное выражение на три части:
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
3️⃣ *: этот квалификатор, следующий за ссылкой на группу \1, указывает, что мы хотели бы видеть повторение группы ноль или более раз.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
😎 Решение:
🪙 1715 вопроса вопроса на Java разработчика
🔒 База собесов | 🔒 База тестовых
Задача: 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* работает.
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.
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;
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
#medium
Задача: 39. Combination Sum
Дан массив уникальных целых чисел candidates и целевое целое число target. Верните список всех уникальных комбинаций из candidates, где выбранные числа в сумме дают target. Комбинации можно возвращать в любом порядке.
Одно и то же число может быть выбрано из массива candidates неограниченное количество раз. Две комбинации считаются уникальными, если частота хотя бы одного из выбранных чисел отличается.
Тестовые случаи сгенерированы таким образом, что количество уникальных комбинаций, дающих в сумме target, меньше 150 комбинаций для данного ввода.
Пример:
👨💻 Алгоритм:
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.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 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]]
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть 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 может быть использовано только один раз в комбинации.
Примечание: Набор решений не должен содержать повторяющихся комбинаций.
Пример:
👨💻 Алгоритм:
1️⃣ Во-первых, мы создаём таблицу счётчиков из предоставленного списка чисел. Затем мы используем эту таблицу счётчиков в процессе обратного поиска, который мы определяем как функцию
2️⃣ При каждом вызове функции обратного поиска мы сначала проверяем, достигли ли мы целевой суммы (то есть
3️⃣ Если осталась сумма для заполнения, мы затем перебираем текущую таблицу счётчиков, чтобы выбрать следующего кандидата. После выбора кандидата мы продолжаем исследование, вызывая функцию
😎 Решение:
🔥 ТОП ВОПРОСОВ С СОБЕСОВ
🔒 База собесов | 🔒 База тестовых
Задача: 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]
]
backtrack(comb, remain, curr, candidate_groups, results). Для сохранения состояния на каждом этапе обратного поиска мы используем несколько параметров в функции:comb: комбинация, которую мы построили на данный момент.remain: оставшаяся сумма, которую нам нужно заполнить, чтобы достичь целевой суммы.curr: курсор, который указывает на текущую группу чисел, используемую из таблицы счётчиков.counter: текущая таблица счётчиков.results: окончательные комбинации, которые достигают целевой суммы.sum(comb) = target), и нужно ли прекратить исследование, потому что сумма текущей комбинации превышает желаемую целевую сумму.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