vault backup: 2025-08-13 14:53:57
This commit is contained in:
@@ -36,210 +36,3 @@
|
||||
8. **Тестирование** - что важно покрыть тестами
|
||||
|
||||
**Время на выполнение:** 25 минут
|
||||
|
||||
---
|
||||
|
||||
# 🧮 Алгоритмическая задача для Middle разработчика
|
||||
|
||||
## 📝 Задача: Планировщик встреч
|
||||
|
||||
### **Условие:**
|
||||
|
||||
У вас есть список встреч, каждая встреча представлена как массив `[start, end]`, где `start` и `end` - время начала и окончания в минутах от начала дня.
|
||||
|
||||
Нужно найти максимальное количество непересекающихся встреч, которые можно провести в одной переговорной комнате.
|
||||
|
||||
### **Примеры:**
|
||||
|
||||
**Пример 1:**
|
||||
|
||||
```
|
||||
Ввод: meetings = [[0,30],[5,10],[15,20]]
|
||||
Вывод: 2
|
||||
Объяснение: Можно выбрать встречи [5,10] и [15,20]
|
||||
```
|
||||
|
||||
**Пример 2:**
|
||||
|
||||
```
|
||||
Ввод: meetings = [[7,10],[2,4]]
|
||||
Вывод: 2
|
||||
Объяснение: Обе встречи не пересекаются
|
||||
```
|
||||
|
||||
**Пример 3:**
|
||||
|
||||
```
|
||||
Ввод: meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]
|
||||
Вывод: 3
|
||||
Объяснение: Можно выбрать [1,5], [8,9], [9,15]
|
||||
```
|
||||
|
||||
### **Ограничения:**
|
||||
|
||||
- `1 <= meetings.length <= 10^4`
|
||||
- `meetings[i].length == 2`
|
||||
- `0 <= starti < endi <= 10^6`
|
||||
|
||||
### **Задание:**
|
||||
|
||||
1. Реализуйте метод `public int maxMeetings(int[][] meetings)`
|
||||
2. Объясните алгоритм и его сложность
|
||||
3. Напишите unit-тесты
|
||||
|
||||
**Время на выполнение:** 15 минут
|
||||
|
||||
---
|
||||
|
||||
# ✅ Решение алгоритмической задачи
|
||||
|
||||
<details> <summary>Показать решение</summary>
|
||||
|
||||
### **Основная идея:**
|
||||
|
||||
Это классическая задача о максимальном количестве непересекающихся интервалов. Используем жадный алгоритм:
|
||||
|
||||
1. Сортируем встречи по времени окончания
|
||||
2. Выбираем встречу с самым ранним окончанием
|
||||
3. Исключаем все пересекающиеся с ней встречи
|
||||
4. Повторяем для оставшихся встреч
|
||||
|
||||
### **Решение:**
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class MeetingScheduler {
|
||||
|
||||
public int maxMeetings(int[][] meetings) {
|
||||
if (meetings == null || meetings.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Сортируем по времени окончания
|
||||
Arrays.sort(meetings, (a, b) -> Integer.compare(a[1], b[1]));
|
||||
|
||||
int count = 0;
|
||||
int lastEndTime = -1;
|
||||
|
||||
for (int[] meeting : meetings) {
|
||||
int startTime = meeting[0];
|
||||
int endTime = meeting[1];
|
||||
|
||||
// Если текущая встреча не пересекается с предыдущей выбранной
|
||||
if (startTime >= lastEndTime) {
|
||||
count++;
|
||||
lastEndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Объяснение алгоритма:**
|
||||
|
||||
1. **Сортировка:** Сортируем все встречи по времени окончания. Это ключевая идея - выбирая встречи с самым ранним окончанием, мы оставляем максимум времени для последующих встреч.
|
||||
|
||||
2. **Жадный выбор:** Проходим по отсортированным встречам и выбираем те, которые не пересекаются с уже выбранными.
|
||||
|
||||
3. **Условие непересечения:** Встреча не пересекается с предыдущей, если её время начала >= времени окончания предыдущей.
|
||||
|
||||
|
||||
### **Временная сложность:** O(n log n) - из-за сортировки
|
||||
|
||||
### **Пространственная сложность:** O(1) - если не считать пространство для сортировки
|
||||
|
||||
### **Пошаговый пример:**
|
||||
|
||||
Для `meetings = [[0,30],[5,10],[15,20]]`:
|
||||
|
||||
1. После сортировки по времени окончания: `[[5,10], [15,20], [0,30]]`
|
||||
2. Выбираем `[5,10]`, `lastEndTime = 10`
|
||||
3. Проверяем `[15,20]`: `15 >= 10` ✓, выбираем, `lastEndTime = 20`
|
||||
4. Проверяем `[0,30]`: `0 < 20` ✗, пропускаем
|
||||
5. Результат: 2 встречи
|
||||
|
||||
Для `meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]`:
|
||||
|
||||
1. После сортировки по времени окончания: `[[1,5], [8,9], [8,9], [5,9], [9,15]]`
|
||||
2. Выбираем `[1,5]`, `lastEndTime = 5`
|
||||
3. Проверяем `[8,9]`: `8 >= 5` ✓, выбираем, `lastEndTime = 9`
|
||||
4. Проверяем `[8,9]`: `8 < 9` ✗, пропускаем
|
||||
5. Проверяем `[5,9]`: `5 < 9` ✗, пропускаем
|
||||
6. Проверяем `[9,15]`: `9 >= 9` ✓, выбираем
|
||||
7. Результат: 3 встречи
|
||||
|
||||
### **Unit тесты:**
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MeetingSchedulerTest {
|
||||
|
||||
private final MeetingScheduler scheduler = new MeetingScheduler();
|
||||
|
||||
@Test
|
||||
void testExample1() {
|
||||
int[][] meetings = {{0,30},{5,10},{15,20}};
|
||||
assertEquals(2, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExample2() {
|
||||
int[][] meetings = {{7,10},{2,4}};
|
||||
assertEquals(2, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExample3() {
|
||||
int[][] meetings = {{1,5},{8,9},{8,9},{5,9},{9,15}};
|
||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyInput() {
|
||||
int[][] meetings = {};
|
||||
assertEquals(0, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullInput() {
|
||||
assertEquals(0, scheduler.maxMeetings(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSingleMeeting() {
|
||||
int[][] meetings = {{1,3}};
|
||||
assertEquals(1, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAllOverlapping() {
|
||||
int[][] meetings = {{1,4},{2,5},{3,6}};
|
||||
assertEquals(1, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoOverlapping() {
|
||||
int[][] meetings = {{1,2},{3,4},{5,6}};
|
||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTouchingMeetings() {
|
||||
int[][] meetings = {{1,3},{3,5},{5,7}};
|
||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Почему жадный алгоритм работает:**
|
||||
|
||||
Выбирая встречу с самым ранним окончанием, мы всегда оставляем максимально возможное время для размещения остальных встреч. Это оптимальная стратегия для данной задачи.
|
||||
|
||||
**Доказательство корректности:** Пусть OPT - оптимальное решение, а GREEDY - решение жадного алгоритма. Можно показать, что GREEDY не хуже OPT путем замены встреч в OPT на встречи из GREEDY без ухудшения результата.
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,204 @@
|
||||
# 🧮 Алгоритмическая задача для Middle разработчика
|
||||
|
||||
## 📝 Задача: Планировщик встреч
|
||||
|
||||
### **Условие:**
|
||||
|
||||
У вас есть список встреч, каждая встреча представлена как массив `[start, end]`, где `start` и `end` - время начала и окончания в минутах от начала дня.
|
||||
|
||||
Нужно найти максимальное количество непересекающихся встреч, которые можно провести в одной переговорной комнате.
|
||||
|
||||
### **Примеры:**
|
||||
|
||||
**Пример 1:**
|
||||
|
||||
```
|
||||
Ввод: meetings = [[0,30],[5,10],[15,20]]
|
||||
Вывод: 2
|
||||
Объяснение: Можно выбрать встречи [5,10] и [15,20]
|
||||
```
|
||||
|
||||
**Пример 2:**
|
||||
|
||||
```
|
||||
Ввод: meetings = [[7,10],[2,4]]
|
||||
Вывод: 2
|
||||
Объяснение: Обе встречи не пересекаются
|
||||
```
|
||||
|
||||
**Пример 3:**
|
||||
|
||||
```
|
||||
Ввод: meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]
|
||||
Вывод: 3
|
||||
Объяснение: Можно выбрать [1,5], [8,9], [9,15]
|
||||
```
|
||||
|
||||
### **Ограничения:**
|
||||
|
||||
- `1 <= meetings.length <= 10^4`
|
||||
- `meetings[i].length == 2`
|
||||
- `0 <= starti < endi <= 10^6`
|
||||
|
||||
### **Задание:**
|
||||
|
||||
1. Реализуйте метод `public int maxMeetings(int[][] meetings)`
|
||||
2. Объясните алгоритм и его сложность
|
||||
3. Напишите unit-тесты
|
||||
|
||||
**Время на выполнение:** 15 минут
|
||||
|
||||
---
|
||||
|
||||
# ✅ Решение алгоритмической задачи
|
||||
|
||||
<details> <summary>Показать решение</summary>
|
||||
|
||||
### **Основная идея:**
|
||||
|
||||
Это классическая задача о максимальном количестве непересекающихся интервалов. Используем жадный алгоритм:
|
||||
|
||||
1. Сортируем встречи по времени окончания
|
||||
2. Выбираем встречу с самым ранним окончанием
|
||||
3. Исключаем все пересекающиеся с ней встречи
|
||||
4. Повторяем для оставшихся встреч
|
||||
|
||||
### **Решение:**
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class MeetingScheduler {
|
||||
|
||||
public int maxMeetings(int[][] meetings) {
|
||||
if (meetings == null || meetings.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Сортируем по времени окончания
|
||||
Arrays.sort(meetings, (a, b) -> Integer.compare(a[1], b[1]));
|
||||
|
||||
int count = 0;
|
||||
int lastEndTime = -1;
|
||||
|
||||
for (int[] meeting : meetings) {
|
||||
int startTime = meeting[0];
|
||||
int endTime = meeting[1];
|
||||
|
||||
// Если текущая встреча не пересекается с предыдущей выбранной
|
||||
if (startTime >= lastEndTime) {
|
||||
count++;
|
||||
lastEndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Объяснение алгоритма:**
|
||||
|
||||
1. **Сортировка:** Сортируем все встречи по времени окончания. Это ключевая идея - выбирая встречи с самым ранним окончанием, мы оставляем максимум времени для последующих встреч.
|
||||
|
||||
2. **Жадный выбор:** Проходим по отсортированным встречам и выбираем те, которые не пересекаются с уже выбранными.
|
||||
|
||||
3. **Условие непересечения:** Встреча не пересекается с предыдущей, если её время начала >= времени окончания предыдущей.
|
||||
|
||||
|
||||
### **Временная сложность:** O(n log n) - из-за сортировки
|
||||
|
||||
### **Пространственная сложность:** O(1) - если не считать пространство для сортировки
|
||||
|
||||
### **Пошаговый пример:**
|
||||
|
||||
Для `meetings = [[0,30],[5,10],[15,20]]`:
|
||||
|
||||
1. После сортировки по времени окончания: `[[5,10], [15,20], [0,30]]`
|
||||
2. Выбираем `[5,10]`, `lastEndTime = 10`
|
||||
3. Проверяем `[15,20]`: `15 >= 10` ✓, выбираем, `lastEndTime = 20`
|
||||
4. Проверяем `[0,30]`: `0 < 20` ✗, пропускаем
|
||||
5. Результат: 2 встречи
|
||||
|
||||
Для `meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]`:
|
||||
|
||||
1. После сортировки по времени окончания: `[[1,5], [8,9], [8,9], [5,9], [9,15]]`
|
||||
2. Выбираем `[1,5]`, `lastEndTime = 5`
|
||||
3. Проверяем `[8,9]`: `8 >= 5` ✓, выбираем, `lastEndTime = 9`
|
||||
4. Проверяем `[8,9]`: `8 < 9` ✗, пропускаем
|
||||
5. Проверяем `[5,9]`: `5 < 9` ✗, пропускаем
|
||||
6. Проверяем `[9,15]`: `9 >= 9` ✓, выбираем
|
||||
7. Результат: 3 встречи
|
||||
|
||||
### **Unit тесты:**
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MeetingSchedulerTest {
|
||||
|
||||
private final MeetingScheduler scheduler = new MeetingScheduler();
|
||||
|
||||
@Test
|
||||
void testExample1() {
|
||||
int[][] meetings = {{0,30},{5,10},{15,20}};
|
||||
assertEquals(2, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExample2() {
|
||||
int[][] meetings = {{7,10},{2,4}};
|
||||
assertEquals(2, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExample3() {
|
||||
int[][] meetings = {{1,5},{8,9},{8,9},{5,9},{9,15}};
|
||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyInput() {
|
||||
int[][] meetings = {};
|
||||
assertEquals(0, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullInput() {
|
||||
assertEquals(0, scheduler.maxMeetings(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSingleMeeting() {
|
||||
int[][] meetings = {{1,3}};
|
||||
assertEquals(1, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAllOverlapping() {
|
||||
int[][] meetings = {{1,4},{2,5},{3,6}};
|
||||
assertEquals(1, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoOverlapping() {
|
||||
int[][] meetings = {{1,2},{3,4},{5,6}};
|
||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTouchingMeetings() {
|
||||
int[][] meetings = {{1,3},{3,5},{5,7}};
|
||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Почему жадный алгоритм работает:**
|
||||
|
||||
Выбирая встречу с самым ранним окончанием, мы всегда оставляем максимально возможное время для размещения остальных встреч. Это оптимальная стратегия для данной задачи.
|
||||
|
||||
**Доказательство корректности:** Пусть OPT - оптимальное решение, а GREEDY - решение жадного алгоритма. Можно показать, что GREEDY не хуже OPT путем замены встреч в OPT на встречи из GREEDY без ухудшения результата.
|
||||
|
||||
</details>
|
||||
Reference in New Issue
Block a user