В этом уроке ты
- Решишь задачу с собеседования в BigTech.
- Освоишь “метод отрезков” для решения задач.
Пример задачи с собеседования
Представь, что ты пришёл на собеседование. Интервьюер начинает озвучивать условие задачи…
Условие
Дан массив отрезков , где segments содержит два числа: segments[i] отрезка. Нужно объединить все пересекающиеся отрезки и вернуть итоговый список из непересекающихся отрезков в порядке возрастания.[начало, конец]
Считаем, что отрезки пересекаются, если у них есть хотя бы одна общая точка.
Пример 1
Ввод: segments = [[2,5],[1,3],[6,7]] Вывод: [[1,5],[6,7]]
Пример 2
Ввод: segments = [[-3,10],[10,100]] Вывод: [[-3,100]]
Оптимальное решение
public class Solution
{
public static bool IsOverlapping(List<int> a, List<int> b)
{
return Math.Max(a[0], b[0]) <= Math.Min(a[1], b[1]);
}
public static List<int> MergeTwoSegments(List<int> a, List<int> b)
{
// отрезки обязательно должны пересекаться
return new List<int> { a[0], Math.Max(a[1], b[1]) };
}
public static List<List<int>> Merge(List<List<int>> segments)
{
// Сортируем отрезки по первой точке
segments.Sort((a, b) => a[0].CompareTo(b[0]));
var result = new List<List<int>> { segments[0] };
for (int i = 1; i < segments.Count; i++)
{
var segment = segments[i];
if (IsOverlapping(result[result.Count - 1], segment))
{
result[result.Count - 1] = MergeTwoSegments(result[result.Count - 1], segment);
}
else
{
result.Add(segment);
}
}
return result;
}
}
#include <vector>
#include <algorithm>
using namespace std;
bool isOverlapping(const vector<int>& a, const vector<int>& b) {
return max(a[0], b[0]) <= min(a[1], b[1]);
}
vector<int> mergeTwoSegments(const vector<int>& a, const vector<int>& b) {
// отрезки обязательно должны пересекаться
return {a[0], max(a[1], b[1])};
}
vector<vector<int>> merge(vector<vector<int>> segments) {
// Сортируем отрезки по первой точке
sort(segments.begin(), segments.end());
vector<vector<int>> result = {segments[0]};
for (size_t i = 1; i < segments.size(); ++i) {
const vector<int>& segment = segments[i];
if (isOverlapping(result.back(), segment)) {
result.back() = mergeTwoSegments(result.back(), segment);
} else {
result.push_back(segment);
}
}
return result;
}
package main
func isOverlapping(a, b []int) bool {
return max(a[0], b[0]) <= min(a[1], b[1])
}
func mergeTwoSegments(a, b []int) []int {
// отрезки обязательно должны пересекаться
return []int{a[0], max(a[1], b[1])}
}
func merge(segments [][]int) [][]int {
// Сортируем отрезки по первой точке
sort.Slice(segments, func(i, j int) bool {
return segments[i][0] < segments[j][0]
})
result := [][]int{segments[0]}
for i := 1; i < len(segments); i++ {
segment := segments[i]
if isOverlapping(result[len(result)-1], segment) {
result[len(result)-1] = mergeTwoSegments(result[len(result)-1], segment)
} else {
result = append(result, segment)
}
}
return result
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
import java.util.*;
public class Solution {
public static boolean isOverlapping(List<Integer> a, List<Integer> b) {
return Math.max(a.get(0), b.get(0)) <= Math.min(a.get(1), b.get(1));
}
public static List<Integer> mergeTwoSegments(List<Integer> a, List<Integer> b) {
// отрезки обязательно должны пересекаться
return Arrays.asList(a.get(0), Math.max(a.get(1), b.get(1)));
}
public static List<List<Integer>> merge(List<List<Integer>> segments) {
// Сортируем отрезки по первой точке
segments.sort(Comparator.comparingInt(a -> a.get(0)));
List<List<Integer>> result = new ArrayList<>();
result.add(segments.get(0));
for (int i = 1; i < segments.size(); i++) {
List<Integer> segment = segments.get(i);
if (isOverlapping(result.get(result.size() - 1), segment)) {
result.set(result.size() - 1, mergeTwoSegments(result.get(result.size() - 1), segment));
} else {
result.add(segment);
}
}
return result;
}
}
from typing import List
def is_overlapping(a: List[int], b: List[int]) -> bool:
return max(a[0], b[0]) <= min(a[1], b[1])
def merge_two_segments(a: List[int], b: List[int]) -> List[int]:
# отрезки обязательно должны пересекаться
return [a[0], max(a[1], b[1])]
def merge(segments: List[List[int]]) -> List[List[int]]:
# Сортируем отрезки по первой точке
segments.sort()
result = [segments[0]]
for i in range(1, len(segments)):
segment = segments[i]
if is_overlapping(result[-1], segment):
result[-1] = merge_two_segments(result[-1], segment)
else:
result.append(segment)
return result
export function isOverlapping(a, b) {
return Math.max(a[0], b[0]) <= Math.min(a[1], b[1]);
}
export function mergeTwoSegments(a, b) {
// отрезки обязательно должны пересекаться
return [a[0], Math.max(a[1], b[1])];
}
export function merge(segments) {
// Сортируем отрезки по первой точке
segments.sort((a, b) => a[0] - b[0]);
let result = [segments[0]];
for (let i = 1; i < segments.length; i++) {
let segment = segments[i];
if (isOverlapping(result[result.length - 1], segment)) {
result[result.length - 1] = mergeTwoSegments(result[result.length - 1], segment);
} else {
result.push(segment);
}
}
return result;
}
Главная идея: сначала сортируем отрезки по начальной координате. Затем формируем результирующий список, начиная с первого отрезка. На каждом шаге проверяем, пересекается ли текущий отрезок с последним отрезком в результате. Если да — расширяем последний отрезок; если нет — добавляем новый.
Оценка сложности
- Время:
O(n*log(n)), гдеn– размер массиваsegments. Основная часть времени уходит на сортировку. - Память:
O(n). Дополнительная память расходуется на результирующий массив и сортировку.
Метод отрезков
Метод отрезков — это общий подход к решению задач, основанный на сортировке отрезков и последующем анализе их пересечений или объединений (в зависимости от условия).
Что дальше
Скорее переходи к практике, чтобы отработать полученные знания и закрепить “метод отрезков”.