Files
second-mind-aep/💼 Работа/Собеседования/Лето 2025/Задачи/🧮 Алгоритмическая задача для Middle разработчика.md
2025-08-18 12:48:52 +04:00

895 lines
29 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 🏷️ Задача 1: Оптимальная маршрутизация сборщика заказов
### **Условие:**
Сборщик заказов должен собрать товары с полок, расположенных в ряд. Каждая полка имеет координату (позицию) и может содержать один или несколько товаров. Сборщик стартует с позиции 0 и должен посетить все полки с товарами, минимизируя пройденное расстояние.
Найдите минимальное расстояние, которое нужно пройти сборщику.
### **Входные данные:**
- `positions` - массив позиций полок с товарами (отсортированный)
### **Тестовые примеры:**
**Пример 1:**
```
Вход: positions = [2, 3, 10, 15]
Выход: 15
Объяснение: Идем от 0 до 15, проходим все полки: 0→2→3→10→15
```
**Пример 2:**
```
Вход: positions = [1, 9]
Выход: 9
Объяснение: 0→1→9 или 0→9→1, оба пути дают расстояние 9
```
**Пример 3:**
```
Вход: positions = [5]
Выход: 5
Объяснение: Просто идем к позиции 5
```
### **Расширенные тестовые данные:**
```
Вход: positions = [1, 2, 5, 8, 12, 16, 20, 25, 30, 35]
Выход: 35
Объяснение: Оптимальный путь 0→1→2→5→8→12→16→20→25→30→35
```
### **Решение:**
```java
public int minDistanceToCollectItems(int[] positions) {
if (positions == null || positions.length == 0) {
return 0;
}
// Поскольку нужно посетить все позиции и они отсортированы,
// оптимально идти от начала до конца
return positions[positions.length - 1];
}
```
**Временная сложность:** O(1)
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как изменится решение, если сборщик может начать с любой позиции?
2. Что если некоторые полки содержат приоритетные товары, которые нужно собрать в первую очередь?
---
## 📦 Задача 2: Укладка коробок разных размеров
### **Условие:**
На складе нужно уложить коробки в стеллаж. Каждая коробка имеет высоту, и стеллаж имеет максимальную высоту H. Коробки можно ставить только одна на другую. Найдите максимальное количество коробок, которые можно уложить в стеллаж.
### **Входные данные:**
- `heights` - массив высот коробок
- `maxHeight` - максимальная высота стеллажа
### **Тестовые примеры:**
**Пример 1:**
```
Вход: heights = [1, 3, 2, 4], maxHeight = 6
Выход: 3
Объяснение: Можем взять коробки высотой [1, 2, 3] = 6
```
**Пример 2:**
```
Вход: heights = [2, 5, 1, 3], maxHeight = 7
Выход: 3
Объяснение: [1, 2, 3] = 6 или [1, 3, 2] = 6, но не можем добавить коробку высотой 5
```
**Пример 3:**
```
Вход: heights = [5, 4, 6], maxHeight = 10
Выход: 2
Объяснение: Максимум можем взять [4, 5] = 9 или [4, 6] = 10
```
### **Расширенные тестовые данные:**
```
Вход: heights = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5], maxHeight = 15
Выход: 6
Объяснение: [1, 1, 2, 2, 3, 3] = 12 или другие комбинации из 6 коробок
```
### **Решение:**
```java
import java.util.Arrays;
public int maxBoxes(int[] heights, int maxHeight) {
if (heights == null || heights.length == 0) {
return 0;
}
// Сортируем коробки по высоте (жадный подход)
Arrays.sort(heights);
int totalHeight = 0;
int count = 0;
for (int height : heights) {
if (totalHeight + height <= maxHeight) {
totalHeight += height;
count++;
} else {
break;
}
}
return count;
}
```
**Временная сложность:** O(n log n) - из-за сортировки
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как изменится алгоритм, если коробки имеют еще и вес, и есть ограничение по весу?
2. Что если коробки нельзя переворачивать и у них есть определенная ориентация?
---
## 🚛 Задача 3: Планирование загрузки грузовиков
### **Условие:**
Склад должен отправить заказы разного веса на грузовиках. Каждый грузовик может везти максимум `capacity` единиц веса. Заказы должны быть отправлены в порядке поступления (нельзя менять порядок). Найдите минимальное количество грузовиков, необходимое для отправки всех заказов.
### **Входные данные:**
- `orders` - массив весов заказов
- `capacity` - грузоподъемность одного грузовика
### **Тестовые примеры:**
**Пример 1:**
```
Вход: orders = [1, 2, 3, 4, 5], capacity = 5
Выход: 4
Объяснение: [1,2] [3] [4] [5] - 4 грузовика
```
**Пример 2:**
```
Вход: orders = [3, 2, 2, 1], capacity = 6
Выход: 2
Объяснение: [3,2] [2,1] - 2 грузовика или [3] [2,2,1] - тоже 2
```
**Пример 3:**
```
Вход: orders = [1, 1, 1, 1, 1], capacity = 3
Выход: 2
Объяснение: [1,1,1] [1,1] - 2 грузовика
```
### **Расширенные тестовые данные:**
```
Вход: orders = [2, 3, 1, 4, 2, 3, 1, 2], capacity = 8
Выход: 3
Объяснение: [2,3,1] [4,2] [3,1,2] - 3 грузовика
```
### **Решение:**
```java
public int minTrucksNeeded(int[] orders, int capacity) {
if (orders == null || orders.length == 0) {
return 0;
}
int trucks = 1;
int currentLoad = 0;
for (int order : orders) {
if (currentLoad + order <= capacity) {
currentLoad += order;
} else {
// Нужен новый грузовик
trucks++;
currentLoad = order;
}
}
return trucks;
}
```
**Временная сложность:** O(n)
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как решить задачу, если можно менять порядок заказов?
2. Что если разные грузовики имеют разную грузоподъемность?
---
# 📊 Задача 4: Анализ пиковых нагрузок склада
## Условие:
Склад ведет учет количества активных заказов в каждый момент времени. Заказ активен с момента поступления до момента отправки (включая момент поступления, но НЕ включая момент отправки). Найдите максимальное количество одновременно активных заказов.
## Входные данные:
1. `intervals` - массив интервалов `[start, end]`, где каждый интервал представляет время жизни заказа
## Тестовые примеры:
### Пример 1:
**Вход:** `intervals = [[1,3],[2,6],[8,10],[15,18]]`
**Выход:** `2`
**Объяснение:** В момент времени 2 активны заказы [1,3] и [2,6]
### Пример 2:
**Вход:** `intervals = [[1,4],[4,5]]`
**Выход:** `1`
**Объяснение:** Заказы не пересекаются по времени (первый заканчивается в момент 4, второй начинается в момент 4)
### Пример 3:
**Вход:** `intervals = [[1,5],[2,3],[4,6],[7,8]]`
**Выход:** `2`
**Объяснение:** Максимальное пересечение в моменты времени 2 и 4, когда активны по 2 заказа
### Расширенные тестовые данные:
**Вход:** `intervals = [[1,10],[2,4],[3,6],[4,8],[5,7],[6,9],[7,12]]`
**Выход:** `4`
**Объяснение:** Максимальное пересечение в районе времени 5-6, когда активны заказы [1,10], [3,6], [4,8], [5,7]
## Решение:
```java
import java.util.*;
public int maxConcurrentOrders(int[][] intervals) {
if (intervals == null || intervals.length == 0) {
return 0;
}
List<int[]> events = new ArrayList<>();
// Создаем события начала и конца заказов
for (int[] interval : intervals) {
events.add(new int[]{interval[0], 1}); // start: +1
events.add(new int[]{interval[1], -1}); // end: -1
}
// ИСПРАВЛЕНИЕ: При равном времени сначала обрабатываем начало заказа (+1), потом конец (-1)
events.sort((a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);
int maxConcurrent = 0;
int currentConcurrent = 0;
for (int[] event : events) {
currentConcurrent += event[1];
maxConcurrent = Math.max(maxConcurrent, currentConcurrent);
}
return maxConcurrent;
}
```
### Альтернативное решение (JavaScript):
```javascript
function maxConcurrentOrders(intervals) {
if (!intervals || intervals.length === 0) {
return 0;
}
let events = [];
// Создаем события начала и конца заказов
for (let interval of intervals) {
events.push([interval[0], 1]); // start: +1
events.push([interval[1], -1]); // end: -1
}
// При равном времени сначала обрабатываем начало заказа (+1), потом конец (-1)
events.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]);
let maxConcurrent = 0;
let currentConcurrent = 0;
for (let event of events) {
currentConcurrent += event[1];
maxConcurrent = Math.max(maxConcurrent, currentConcurrent);
}
return maxConcurrent;
}
```
## Ключевые исправления:
1. **Сортировка событий:** При одинаковом времени сначала обрабатываем события начала заказа (+1), затем события окончания (-1). Это важно, так как заказ считается активным в момент начала, но НЕ активным в момент окончания.
2. **Объяснения к примерам:** Исправлены неточности в объяснениях результатов.
3. **Расширенный пример:** Пересчитан правильный результат.
## Сложность:
- **Временная сложность:** O(n log n) - сортировка событий
- **Пространственная сложность:** O(n) - список событий
## Дополнительные вопросы:
### 1. Как найти конкретный момент времени, когда достигается максимум?
```java
public List<Integer> findPeakTimes(int[][] intervals) {
// ... тот же код до цикла обработки событий
List<Integer> peakTimes = new ArrayList<>();
int maxConcurrent = 0;
int currentConcurrent = 0;
for (int[] event : events) {
currentConcurrent += event[1];
if (currentConcurrent > maxConcurrent) {
maxConcurrent = currentConcurrent;
peakTimes.clear();
peakTimes.add(event[0]);
} else if (currentConcurrent == maxConcurrent && event[1] == 1) {
peakTimes.add(event[0]);
}
}
return peakTimes;
}
```
### 2. Как адаптировать алгоритм для поиска минимального количества рабочих станций?
Алгоритм остается тем же - максимальное количество одновременно активных заказов и есть минимальное количество рабочих станций, необходимых для обработки всех заказов без задержек.
## Важные моменты:
- Заказ активен в интервале `[start, end)` - включая start, но исключая end
- При одновременном начале и окончании заказов сначала обрабатываем начало
- Алгоритм основан на технике "sweep line" (сканирующая прямая)
---
## 🔍 Задача 5: Поиск товаров в секционированном складе
### **Условие:**
Склад разделен на секции, в каждой секции товары отсортированы по ID. Некоторые секции могут быть пустыми. Нужно найти секцию, содержащую товар с заданным ID.
### **Входные данные:**
- `sections` - двумерный массив, где каждая строка представляет секцию с отсортированными ID товаров
- `target` - искомый ID товара
### **Тестовые примеры:**
**Пример 1:**
```
Вход: sections = [[1,4,7,11],[],[2,5,9,10,13],[3,6,8,16]], target = 5
Выход: 2
Объяснение: Товар с ID 5 находится в секции 2 (индексация с 0)
```
**Пример 2:**
```
Вход: sections = [[1,3,5],[2,4,6]], target = 7
Выход: -1
Объяснение: Товар с ID 7 не найден
```
**Пример 3:**
```
Вход: sections = [[],[1,2,3],[4,5,6]], target = 1
Выход: 1
Объяснение: Товар с ID 1 находится в секции 1
```
### **Расширенные тестовые данные:**
```
Вход: sections = [[1,5,9,13],[2,6,10,14],[3,7,11,15],[4,8,12,16]], target = 11
Выход: 2
Объяснение: Товар с ID 11 находится в секции 2
```
### **Решение:**
```java
public int searchInSections(int[][] sections, int target) {
if (sections == null || sections.length == 0) {
return -1;
}
for (int i = 0; i < sections.length; i++) {
if (sections[i].length == 0) {
continue;
}
// Проверяем, может ли target быть в этой секции
if (target >= sections[i][0] && target <= sections[i][sections[i].length - 1]) {
// Выполняем бинарный поиск в секции
if (binarySearch(sections[i], target)) {
return i;
}
}
}
return -1;
}
private boolean binarySearch(int[] array, int target) {
int left = 0, right = array.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (array[mid] == target) {
return true;
} else if (array[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
```
**Временная сложность:** O(m + log n), где m - количество секций, n - максимальный размер секции
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как изменить алгоритм, чтобы найти все секции, содержащие товар?
2. Что если секции не отсортированы и нужно оптимизировать поиск?
---
## 🏃‍♂️ Задача 6: Оптимизация маршрута между зонами склада
### **Условие:**
Склад имеет зоны, соединенные коридорами. Сотрудник должен пройти через определенные зоны в заданном порядке. Каждый переход между соседними зонами занимает 1 единицу времени. Зоны пронумерованы последовательно. Найдите минимальное время для прохождения всех зон в порядке.
### **Входные данные:**
- `zones` - массив номеров зон в порядке посещения
### **Тестовые примеры:**
**Пример 1:**
```
Вход: zones = [1, 3, 6, 7]
Выход: 6
Объяснение: 1→3 (2 шага) + 3→6 (3 шага) + 6→7 (1 шаг) = 6
```
**Пример 2:**
```
Вход: zones = [2, 2, 2]
Выход: 0
Объяснение: Остаемся в той же зоне, время = 0
```
**Пример 3:**
```
Вход: zones = [10, 5, 8]
Выход: 8
Объяснение: 10→5 (5 шагов) + 5→8 (3 шага) = 8
```
### **Расширенные тестовые данные:**
```
Вход: zones = [1, 10, 3, 8, 2, 15, 7]
Выход: 35
Объяснение: |1-10| + |10-3| + |3-8| + |8-2| + |2-15| + |15-7| = 9+7+5+6+13+8 = 48
```
### **Решение:**
```java
public int minTimeToVisitZones(int[] zones) {
if (zones == null || zones.length <= 1) {
return 0;
}
int totalTime = 0;
for (int i = 1; i < zones.length; i++) {
totalTime += Math.abs(zones[i] - zones[i - 1]);
}
return totalTime;
}
```
**Временная сложность:** O(n)
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как решить задачу, если можно изменить порядок посещения зон?
2. Что если некоторые переходы между зонами заблокированы?
---
## 📋 Задача 7: Группировка заказов по приоритету
### **Условие:**
На складе есть заказы с разными приоритетами (числа от 1 до k). Заказы одного приоритета должны обрабатываться вместе. Найдите количество групп заказов одинакового приоритета, стоящих подряд.
### **Входные данные:**
- `priorities` - массив приоритетов заказов
### **Тестовые примеры:**
**Пример 1:**
```
Вход: priorities = [1, 1, 2, 2, 3, 1, 1]
Выход: 4
Объяснение: Группы: [1,1], [2,2], [3], [1,1]
```
**Пример 2:**
```
Вход: priorities = [1, 2, 1, 2]
Выход: 4
Объяснение: Группы: [1], [2], [1], [2]
```
**Пример 3:**
```
Вход: priorities = [3, 3, 3, 3]
Выход: 1
Объяснение: Одна группа: [3,3,3,3]
```
### **Расширенные тестовые данные:**
```
Вход: priorities = [1, 1, 2, 3, 3, 3, 2, 2, 1, 1, 1, 4, 4]
Выход: 6
Объяснение: [1,1], [2], [3,3,3], [2,2], [1,1,1], [4,4]
```
### **Решение:**
```java
public int countPriorityGroups(int[] priorities) {
if (priorities == null || priorities.length == 0) {
return 0;
}
int groups = 1;
for (int i = 1; i < priorities.length; i++) {
if (priorities[i] != priorities[i - 1]) {
groups++;
}
}
return groups;
}
```
**Временная сложность:** O(n)
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как найти самую длинную группу заказов одного приоритета?
2. Что если нужно найти количество различных приоритетов в массиве?
---
## 🎯 Задача 8: Поиск оптимального места хранения
### **Условие:**
На складе есть ряд мест хранения, некоторые заняты (`1`), некоторые свободны (`0`). Нужно найти самую длинную последовательность свободных мест для размещения крупногабаритного товара.
### **Входные данные:**
- `storage` - массив состояний мест хранения (0 - свободно, 1 - занято)
### **Тестовые примеры:**
**Пример 1:**
```
Вход: storage = [1, 0, 0, 0, 1, 0, 0]
Выход: 3
Объяснение: Последовательность [0,0,0] имеет длину 3
```
**Пример 2:**
```
Вход: storage = [0, 0, 1, 0, 0, 0, 0, 1]
Выход: 4
Объяснение: Последовательность [0,0,0,0] имеет длину 4
```
**Пример 3:**
```
Вход: storage = [1, 1, 1, 1]
Выход: 0
Объяснение: Нет свободных мест
```
### **Расширенные тестовые данные:**
```
Вход: storage = [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]
Выход: 5
Объяснение: Последовательность [0,0,0,0,0] имеет максимальную длину 5
```
### **Решение:**
```java
public int longestFreeSpace(int[] storage) {
if (storage == null || storage.length == 0) {
return 0;
}
int maxLength = 0;
int currentLength = 0;
for (int space : storage) {
if (space == 0) {
currentLength++;
maxLength = Math.max(maxLength, currentLength);
} else {
currentLength = 0;
}
}
return maxLength;
}
```
**Временная сложность:** O(n)
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как найти позицию начала самой длинной последовательности?
2. Что если можно освободить k занятых мест для увеличения свободного пространства?
---
## 🔄 Задача 9: Ротация стеллажей для доступа к товарам
### **Условие:**
Автоматический стеллаж представляет собой кольцевую структуру с ячейками. Чтобы получить доступ к товару в определенной ячейке, стеллаж можно поворачивать влево или вправо. Каждый поворот на одну позицию занимает 1 единицу времени. Найдите минимальное время для доступа ко всем товарам в заданном порядке.
### **Входные данные:**
- `n` - количество ячеек в стеллаже (нумерация 0 до n-1)
- `targets` - порядок ячеек для доступа к товарам
- `start` - начальная позиция
### **Тестовые примеры:**
**Пример 1:**
```
Вход: n = 4, targets = [0, 1, 3], start = 0
Выход: 3
Объяснение: 0→1 (1 шаг) + 1→3 (2 шага) = 3
```
**Пример 2:**
```
Вход: n = 5, targets = [4, 0, 2], start = 1
Выход: 4
Объяснение: 1→4 (2 шага) + 4→0 (1 шаг) + 0→2 (2 шага) = 5
Проверим: 1→4 влево (3 шага) или вправо (2 шага) - выбираем 2
4→0 влево (1 шаг) или вправо (4 шага) - выбираем 1
0→2 (2 шага в любую сторону) = 2+1+2 = 5
```
**Пример 3:**
```
Вход: n = 3, targets = [2], start = 1
Выход: 1
Объяснение: 1→2 (1 шаг в любую сторону)
```
### **Расширенные тестовые данные:**
```
Вход: n = 8, targets = [7, 1, 3, 5], start = 0
Выход: 8
```
### **Решение:**
```java
public int minTimeToAccessItems(int n, int[] targets, int start) {
if (targets == null || targets.length == 0) {
return 0;
}
int totalTime = 0;
int currentPos = start;
for (int target : targets) {
int clockwise = (target - currentPos + n) % n;
int counterclockwise = (currentPos - target + n) % n;
totalTime += Math.min(clockwise, counterclockwise);
currentPos = target;
}
return totalTime;
}
```
**Временная сложность:** O(m), где m - количество целевых позиций
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как изменится решение, если можно изменить порядок доступа к товарам?
2. Что если стеллаж может вращаться с разной скоростью в разных направлениях?
---
## 📈 Задача 10: Анализ производительности конвейера
### **Условие:**
На конвейере обрабатываются товары. В каждый момент времени известна скорость конвейера. Найдите период времени, когда средняя скорость была максимальной для окна размером k.
### **Входные данные:**
- `speeds` - массив скоростей конвейера в разные моменты времени
- `k` - размер временного окна
### **Тестовые примеры:**
**Пример 1:**
```
Вход: speeds = [1, 3, 2, 6, 4], k = 3
Выход: 2
Объяснение: Окна: [1,3,2]=6, [3,2,6]=11, [2,6,4]=12. Максимум в позиции 2
```
**Пример 2:**
```
Вход: speeds = [5, 2, 1, 8, 9], k = 2
Выход: 3
Объяснение: Окна: [5,2]=7, [2,1]=3, [1,8]=9, [8,9]=17. Максимум в позиции 3
```
**Пример 3:**
```
Вход: speeds = [1, 2, 3, 4, 5], k = 1
Выход: 4
Объяснение: Максимальная скорость 5 в позиции 4
```
### **Расширенные тестовые данные:**
```
Вход: speeds = [2, 1, 3, 4, 6, 7, 8, 1, 2], k = 4
Выход: 4
Объяснение: Нужно найти окно размером 4 с максимальной суммой
```
### **Решение:**
```java
public int maxAverageWindow(int[] speeds, int k) {
if (speeds == null || speeds.length < k) {
return -1;
}
// Вычисляем сумму первого окна
int windowSum = 0;
for (int i = 0; i < k; i++) {
windowSum += speeds[i];
}
int maxSum = windowSum;
int maxIndex = 0;
// Скользящее окно
for (int i = k; i < speeds.length; i++) {
windowSum = windowSum - speeds[i - k] + speeds[i];
if (windowSum > maxSum) {
maxSum = windowSum;
maxIndex = i - k + 1;
}
}
return maxIndex;
}
```
**Временная сложность:** O(n)
**Пространственная сложность:** O(1)
### **Дополнительные вопросы:**
1. Как найти все окна с максимальной средней скоростью?
2. Что если нужно найти окно с минимальной дисперсией скоростей?
---
## 📝 Инструкции по использованию
### **Для интервьюера:**
1. **Выбор задач:** Для Middle разработчика рекомендуется 2-3 задачи на 30-45 минут
2. **Оценка:** Обращайте внимание на:
- Правильность алгоритма
- Оптимальность решения
- Качество кода
- Обработку граничных случаев
- Ответы на дополнительные вопросы
3. **Подсказки:** Если кандидат затрудняется, можно дать наводящие вопросы по теме складской логистики
### **Уровни сложности:**
- **Easy:** Задачи 1