← Назад к курсу

Решение задач с LeetCode TOP-150 (Блок 4/5: Задачи 91-120)

91. Clone Graph

Описание: Клонировать неориентированный граф.

class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []

def cloneGraph(node):
    """
    Клонирование неориентированного графа
    """
    if not node:
        return None
    
    # Словарь для хранения копий узлов
    clones = {}
    
    def dfs(original):
        """Рекурсивно клонируем граф"""
        if original in clones:
            return clones[original]
        
        # Создаем копию узла
        clone = Node(original.val)
        clones[original] = clone
        
        # Рекурсивно клонируем соседей
        for neighbor in original.neighbors:
            clone.neighbors.append(dfs(neighbor))
        
        return clone
    
    return dfs(node)

# Итеративное решение
def cloneGraphIterative(node):
    if not node:
        return None
    
    clones = {node: Node(node.val)}
    stack = [node]
    
    while stack:
        current = stack.pop()
        
        for neighbor in current.neighbors:
            if neighbor not in clones:
                clones[neighbor] = Node(neighbor.val)
                stack.append(neighbor)
            
            clones[current].neighbors.append(clones[neighbor])
    
    return clones[node]

92. Evaluate Division

Описание: Вычислить результаты уравнений вида a/b = values[i].

from collections import defaultdict, deque

def calcEquation(equations, values, queries):
    """
    Вычисление результатов уравнений
    """
    # Строим граф отношений
    graph = defaultdict(dict)
    
    for (a, b), value in zip(equations, values):
        graph[a][b] = value      # a / b = value
        graph[b][a] = 1.0 / value  # b / a = 1/value
    
    def bfs(start, end):
        """Поиск пути от start до end"""
        if start not in graph or end not in graph:
            return -1.0
        
        if start == end:
            return 1.0
        
        queue = deque([(start, 1.0)])  # (node, текущий продукт)
        visited = set([start])
        
        while queue:
            node, product = queue.popleft()
            
            if node == end:
                return product
            
            for neighbor, value in graph[node].items():
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append((neighbor, product * value))
        
        return -1.0
    
    # Обрабатываем все запросы
    results = []
    for a, b in queries:
        results.append(bfs(a, b))
    
    return results

93. Course Schedule

Описание: Проверить возможность завершения всех курсов (задача о цикле в графе).

def canFinish(numCourses, prerequisites):
    """
    Проверка возможности завершения всех курсов (топологическая сортировка)
    """
    # Строим граф и считаем входящие степени
    graph = [[] for _ in range(numCourses)]
    indegree = [0] * numCourses
    
    for course, prereq in prerequisites:
        graph[prereq].append(course)
        indegree[course] += 1
    
    # Находим узлы с нулевой входящей степенью
    queue = deque([i for i in range(numCourses) if indegree[i] == 0])
    completed = 0
    
    while queue:
        course = queue.popleft()
        completed += 1
        
        # Уменьшаем входящую степень соседей
        for neighbor in graph[course]:
            indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                queue.append(neighbor)
    
    return completed == numCourses

94. Course Schedule II

Описание: Найти порядок прохождения курсов.

def findOrder(numCourses, prerequisites):
    """
    Нахождение порядка прохождения курсов
    """
    # Строим граф
    graph = [[] for _ in range(numCourses)]
    indegree = [0] * numCourses
    
    for course, prereq in prerequisites:
        graph[prereq].append(course)
        indegree[course] += 1
    
    # Топологическая сортировка
    queue = deque([i for i in range(numCourses) if indegree[i] == 0])
    order = []
    
    while queue:
        course = queue.popleft()
        order.append(course)
        
        for neighbor in graph[course]:
            indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                queue.append(neighbor)
    
    # Проверяем, что все курсы пройдены
    if len(order) == numCourses:
        return order
    else:
        return []

95. Snakes and Ladders

Описание: Найти минимальное количество ходов в игре "Змеи и лестницы".

def snakesAndLadders(board):
    """
    Минимальное количество ходов в игре "Змеи и лестницы"
    """
    n = len(board)
    
    def get_position(square):
        """Преобразует номер клетки в координаты на доске"""
        row = (square - 1) // n
        col = (square - 1) % n
        
        # Нечетные строки идут справа налево
        if row % 2 == 1:
            col = n - 1 - col
        
        # Нумерация строк снизу вверх
        row = n - 1 - row
        
        return row, col
    
    # BFS для поиска кратчайшего пути
    visited = [False] * (n * n + 1)
    queue = deque([(1, 0)])  # (квадрат, ходы)
    visited[1] = True
    
    while queue:
        square, moves = queue.popleft()
        
        # Достигли цели
        if square == n * n:
            return moves
        
        # Пытаемся сделать от 1 до 6 шагов
        for step in range(1, 7):
            next_square = square + step
            
            if next_square > n * n:
                break
            
            # Получаем координаты на доске
            r, c = get_position(next_square)
            
            # Если есть змея или лестница
            if board[r][c] != -1:
                next_square = board[r][c]
            
            # Если еще не посещали эту клетку
            if not visited[next_square]:
                visited[next_square] = True
                queue.append((next_square, moves + 1))
    
    return -1

96. Minimum Genetic Mutation

Описание: Найти минимальное количество мутаций для преобразования одной строки ДНК в другую.

from collections import deque

def minMutation(startGene, endGene, bank):
    """
    Минимальное количество мутаций для преобразования гена
    """
    # Преобразуем bank в множество для быстрого поиска
    bank_set = set(bank)
    
    if endGene not in bank_set:
        return -1
    
    # Возможные мутации для каждого положения
    mutations = ['A', 'C', 'G', 'T']
    
    # BFS для поиска кратчайшего пути
    queue = deque([(startGene, 0)])
    visited = set([startGene])
    
    while queue:
        gene, steps = queue.popleft()
        
        if gene == endGene:
            return steps
        
        # Генерируем все возможные мутации
        for i in range(len(gene)):
            for mut in mutations:
                if mut == gene[i]:
                    continue
                
                # Создаем новую строку с мутацией
                new_gene = gene[:i] + mut + gene[i+1:]
                
                if new_gene in bank_set and new_gene not in visited:
                    visited.add(new_gene)
                    queue.append((new_gene, steps + 1))
    
    return -1

97. Word Ladder

Описание: Найти кратчайшую последовательность преобразований от одного слова к другому.

from collections import deque
from typing import List

def ladderLength(beginWord: str, endWord: str, wordList: List[str]) -> int:
    """
    Кратчайшая последовательность преобразований слов
    """
    word_set = set(wordList)
    
    if endWord not in word_set:
        return 0
    
    # BFS для поиска кратчайшего пути
    queue = deque([(beginWord, 1)])
    visited = set([beginWord])
    
    while queue:
        word, length = queue.popleft()
        
        if word == endWord:
            return length
        
        # Генерируем все возможные преобразования
        for i in range(len(word)):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                if c == word[i]:
                    continue
                
                new_word = word[:i] + c + word[i+1:]
                
                if new_word in word_set and new_word not in visited:
                    visited.add(new_word)
                    queue.append((new_word, length + 1))
    
    return 0

98. Implement Trie (Prefix Tree)

Описание: Реализовать структуру данных Trie (префиксное дерево).

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self):
        """
        Инициализация Trie
        """
        self.root = TrieNode()
    
    def insert(self, word: str) -> None:
        """
        Вставка слова в Trie
        """
        node = self.root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
        node.is_end = True
    
    def search(self, word: str) -> bool:
        """
        Поиск слова в Trie
        """
        node = self.root
        for char in word:
            if char not in node.children:
                return False
            node = node.children[char]
        return node.is_end
    
    def startsWith(self, prefix: str) -> bool:
        """
        Проверка, есть ли слова с данным префиксом
        """
        node = self.root
        for char in prefix:
            if char not in node.children:
                return False
            node = node.children[char]
        return True

99. Design Add and Search Words Data Structure

Описание: Реализовать структуру данных для добавления и поиска слов с поддержкой '.' как wildcard.

class WordDictionary:
    def __init__(self):
        """
        Инициализация структуры данных
        """
        self.trie = {}
    
    def addWord(self, word: str) -> None:
        """
        Добавление слова
        """
        node = self.trie
        for char in word:
            if char not in node:
                node[char] = {}
            node = node[char]
        node['#'] = True  # Маркер конца слова
    
    def search(self, word: str) -> bool:
        """
        Поиск слова (поддерживает '.' как любой символ)
        """
        def search_in_node(word, node):
            """Рекурсивный поиск"""
            for i, char in enumerate(word):
                if char not in node:
                    # Если символ '.', проверяем все возможные ветви
                    if char == '.':
                        for child in node:
                            if child != '#' and search_in_node(word[i+1:], node[child]):
                                return True
                    return False
                else:
                    node = node[char]
            return '#' in node
        
        return search_in_node(word, self.trie)

100. Word Search II

Описание: Найти все слова из списка на игровом поле слов (word search).

class TrieNode:
    def __init__(self):
        self.children = {}
        self.word = None  # Храним слово в конце узла

def findWords(board, words):
    """
    Поиск всех слов на игровом поле
    """
    # Строим Trie из слов
    root = TrieNode()
    for word in words:
        node = root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
        node.word = word
    
    rows, cols = len(board), len(board[0])
    result = []
    
    def backtrack(i, j, node):
        """Рекурсивный поиск слов"""
        char = board[i][j]
        
        # Проверяем, есть ли такой символ в Trie
        if char not in node.children:
            return
        
        # Переходим к следующему узлу
        next_node = node.children[char]
        
        # Если нашли слово
        if next_node.word:
            result.append(next_node.word)
            next_node.word = None  # Предотвращаем дубликаты
        
        # Помечаем ячейку как посещенную
        board[i][j] = '#'
        
        # Рекурсивно ищем в соседних ячейках
        directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        for dx, dy in directions:
            ni, nj = i + dx, j + dy
            if 0 <= ni < rows and 0 <= nj < cols and board[ni][nj] != '#':
                backtrack(ni, nj, next_node)
        
        # Восстанавливаем ячейку
        board[i][j] = char
        
        # Оптимизация: удаляем листья, которые больше не нужны
        if not next_node.children:
            del node.children[char]
    
    # Запускаем поиск из каждой ячейки
    for i in range(rows):
        for j in range(cols):
            backtrack(i, j, root)
    
    return result

101. Letter Combinations of a Phone Number

Описание: Сгенерировать все возможные комбинации букв для телефонного номера.

def letterCombinations(digits):
    """
    Генерация всех комбинаций букв для телефонного номера
    """
    if not digits:
        return []
    
    # Соответствие цифр и букв
    phone_map = {
        '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
        '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
    }
    
    result = []
    
    def backtrack(index, current):
        """Рекурсивная генерация комбинаций"""
        if index == len(digits):
            result.append(''.join(current))
            return
        
        digit = digits[index]
        letters = phone_map[digit]
        
        for letter in letters:
            current.append(letter)
            backtrack(index + 1, current)
            current.pop()  # Backtrack
    
    backtrack(0, [])
    return result

102. Combinations

Описание: Сгенерировать все комбинации чисел от 1 до n размера k.

def combine(n, k):
    """
    Генерация всех комбинаций чисел от 1 до n размера k
    """
    result = []
    
    def backtrack(start, current):
        """Рекурсивная генерация комбинаций"""
        if len(current) == k:
            result.append(current[:])
            return
        
        # Оптимизация: не хватает элементов для заполнения
        if len(current) + (n - start + 1) < k:
            return
        
        for i in range(start, n + 1):
            current.append(i)
            backtrack(i + 1, current)
            current.pop()  # Backtrack
    
    backtrack(1, [])
    return result

103. Permutations

Описание: Сгенерировать все перестановки массива.

def permute(nums):
    """
    Генерация всех перестановок массива
    """
    result = []
    
    def backtrack(first):
        """Рекурсивная генерация перестановок"""
        if first == len(nums):
            result.append(nums[:])
            return
        
        for i in range(first, len(nums)):
            # Меняем местами
            nums[first], nums[i] = nums[i], nums[first]
            backtrack(first + 1)
            # Возвращаем обратно (backtrack)
            nums[first], nums[i] = nums[i], nums[first]
    
    backtrack(0)
    return result

# Альтернативное решение с дополнительной памятью
def permute2(nums):
    result = []
    
    def backtrack(current, remaining):
        if not remaining:
            result.append(current[:])
            return
        
        for i in range(len(remaining)):
            current.append(remaining[i])
            backtrack(current, remaining[:i] + remaining[i+1:])
            current.pop()
    
    backtrack([], nums)
    return result

104. Combination Sum

Описание: Найти все уникальные комбинации чисел, сумма которых равна target.

def combinationSum(candidates, target):
    """
    Нахождение всех комбинаций чисел с заданной суммой
    """
    result = []
    
    def backtrack(start, current, current_sum):
        """Рекурсивный поиск комбинаций"""
        if current_sum == target:
            result.append(current[:])
            return
        
        if current_sum > target:
            return
        
        for i in range(start, len(candidates)):
            current.append(candidates[i])
            backtrack(i, current, current_sum + candidates[i])
            current.pop()  # Backtrack
    
    backtrack(0, [], 0)
    return result

105. N-Queens II

Описание: Подсчитать количество различных решений задачи N ферзей.

def totalNQueens(n):
    """
    Подсчет количества решений задачи N ферзей
    """
    def backtrack(row, cols, diag1, diag2):
        """Рекурсивная расстановка ферзей"""
        if row == n:
            return 1
        
        count = 0
        available_positions = ((1 << n) - 1) & (~(cols | diag1 | diag2))
        
        while available_positions:
            # Берем младший установленный бит
            position = available_positions & -available_positions
            available_positions -= position
            
            count += backtrack(
                row + 1,
                cols | position,
                (diag1 | position) << 1,
                (diag2 | position) >> 1
            )
        
        return count
    
    return backtrack(0, 0, 0, 0)

106. Generate Parentheses

Описание: Сгенерировать все правильные комбинации скобок длины 2n.

def generateParenthesis(n):
    """
    Генерация всех правильных комбинаций скобок
    """
    result = []
    
    def backtrack(current, open_count, close_count):
        """Рекурсивная генерация скобок"""
        if len(current) == 2 * n:
            result.append(''.join(current))
            return
        
        # Можем добавить открывающую скобку
        if open_count < n:
            current.append('(')
            backtrack(current, open_count + 1, close_count)
            current.pop()
        
        # Можем добавить закрывающую скобку
        if close_count < open_count:
            current.append(')')
            backtrack(current, open_count, close_count + 1)
            current.pop()
    
    backtrack([], 0, 0)
    return result

107. Word Search

Описание: Проверить, существует ли слово на игровом поле букв.

def exist(board, word):
    """
    Проверка существования слова на игровом поле
    """
    rows, cols = len(board), len(board[0])
    
    def backtrack(i, j, index):
        """Рекурсивный поиск слова"""
        # Проверяем границы и совпадение букв
        if i < 0 or i >= rows or j < 0 or j >= cols or board[i][j] != word[index]:
            return False
        
        # Если это последняя буква слова
        if index == len(word) - 1:
            return True
        
        # Помечаем ячейку как посещенную
        temp = board[i][j]
        board[i][j] = '#'
        
        # Ищем следующую букву в соседних ячейках
        found = (backtrack(i + 1, j, index + 1) or
                 backtrack(i - 1, j, index + 1) or
                 backtrack(i, j + 1, index + 1) or
                 backtrack(i, j - 1, index + 1))
        
        # Восстанавливаем ячейку
        board[i][j] = temp
        
        return found
    
    # Запускаем поиск из каждой ячейки
    for i in range(rows):
        for j in range(cols):
            if backtrack(i, j, 0):
                return True
    
    return False

108. Convert Sorted Array to Binary Search Tree

Описание: Преобразовать отсортированный массив в сбалансированное BST.

def sortedArrayToBST(nums):
    """
    Преобразование отсортированного массива в сбалансированное BST
    """
    def build_tree(left, right):
        """Рекурсивное построение дерева"""
        if left > right:
            return None
        
        # Средний элемент - корень
        mid = (left + right) // 2
        root = TreeNode(nums[mid])
        
        # Рекурсивно строим левое и правое поддеревья
        root.left = build_tree(left, mid - 1)
        root.right = build_tree(mid + 1, right)
        
        return root
    
    return build_tree(0, len(nums) - 1)

109. Sort List

Описание: Отсортировать связный список за O(n log n) времени и O(1) памяти.

def sortList(head):
    """
    Сортировка связного списка (merge sort)
    """
    if not head or not head.next:
        return head
    
    # Находим середину списка
    def get_middle(node):
        """Находит середину списка (медленный и быстрый указатель)"""
        slow, fast = node, node.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow
    
    # Слияние двух отсортированных списков
    def merge(left, right):
        """Слияние двух отсортированных списков"""
        dummy = ListNode(0)
        current = dummy
        
        while left and right:
            if left.val <= right.val:
                current.next = left
                left = left.next
            else:
                current.next = right
                right = right.next
            current = current.next
        
        # Добавляем оставшиеся узлы
        if left:
            current.next = left
        if right:
            current.next = right
        
        return dummy.next
    
    # Рекурсивная сортировка
    mid = get_middle(head)
    right_head = mid.next
    mid.next = None  # Разделяем список
    
    left = sortList(head)
    right = sortList(right_head)
    
    return merge(left, right)

110. Construct Quad Tree

Описание: Построить Quad Tree из бинарной матрицы.

class Node:
    def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight):
        self.val = val
        self.isLeaf = isLeaf
        self.topLeft = topLeft
        self.topRight = topRight
        self.bottomLeft = bottomLeft
        self.bottomRight = bottomRight

def construct(grid):
    """
    Построение Quad Tree из матрицы
    """
    def build(r1, c1, r2, c2):
        """Рекурсивное построение дерева"""
        # Проверяем, все ли значения в квадрате одинаковы
        all_same = True
        first_val = grid[r1][c1]
        
        for i in range(r1, r2):
            for j in range(c1, c2):
                if grid[i][j] != first_val:
                    all_same = False
                    break
            if not all_same:
                break
        
        # Если все значения одинаковы, создаем лист
        if all_same:
            return Node(first_val == 1, True, None, None, None, None)
        
        # Иначе делим на 4 части
        row_mid = (r1 + r2) // 2
        col_mid = (c1 + c2) // 2
        
        topLeft = build(r1, c1, row_mid, col_mid)
        topRight = build(r1, col_mid, row_mid, c2)
        bottomLeft = build(row_mid, c1, r2, col_mid)
        bottomRight = build(row_mid, col_mid, r2, c2)
        
        return Node(False, False, topLeft, topRight, bottomLeft, bottomRight)
    
    n = len(grid)
    return build(0, 0, n, n)

111. Merge k Sorted Lists

Описание: Объединить k отсортированных связных списков в один отсортированный список.

import heapq

def mergeKLists(lists):
    """
    Объединение k отсортированных связных списков
    """
    # Минимальная куча
    heap = []
    
    # Добавляем первые элементы каждого списка в кучу
    for i, lst in enumerate(lists):
        if lst:
            heapq.heappush(heap, (lst.val, i, lst))
    
    dummy = ListNode(0)
    current = dummy
    
    while heap:
        val, idx, node = heapq.heappop(heap)
        
        # Добавляем узел в результат
        current.next = node
        current = current.next
        
        # Добавляем следующий элемент из этого списка
        if node.next:
            heapq.heappush(heap, (node.next.val, idx, node.next))
    
    return dummy.next

112. Maximum Subarray

Описание: Найти подмассив с максимальной суммой (алгоритм Кадане).

def maxSubArray(nums):
    """
    Нахождение подмассива с максимальной суммой (алгоритм Кадане)
    """
    if not nums:
        return 0
    
    max_sum = nums[0]
    current_sum = nums[0]
    
    for num in nums[1:]:
        # Либо начинаем новый подмассив, либо продолжаем текущий
        current_sum = max(num, current_sum + num)
        max_sum = max(max_sum, current_sum)
    
    return max_sum

113. Maximum Sum Circular Subarray

Описание: Найти максимальную сумму подмассива в циклическом массиве.

def maxSubarraySumCircular(nums):
    """
    Максимальная сумма подмассива в циклическом массиве
    """
    # Случай 1: максимальный подмассив не пересекает границу
    max_kadane = nums[0]
    current_max = nums[0]
    total_sum = nums[0]
    
    # Случай 2: минимальный подмассив (для циклического случая)
    min_kadane = nums[0]
    current_min = nums[0]
    
    for num in nums[1:]:
        # Стандартный алгоритм Кадане для максимума
        current_max = max(num, current_max + num)
        max_kadane = max(max_kadane, current_max)
        
        # Алгоритм Кадане для минимума
        current_min = min(num, current_min + num)
        min_kadane = min(min_kadane, current_min)
        
        total_sum += num
    
    # Если все числа отрицательные
    if max_kadane <= 0:
        return max_kadane
    
    # Максимум из обычного подмассива и циклического
    return max(max_kadane, total_sum - min_kadane)

114. Search Insert Position

Описание: Найти позицию для вставки элемента в отсортированный массив.

def searchInsert(nums, target):
    """
    Поиск позиции для вставки в отсортированный массив
    """
    left, right = 0, len(nums) - 1
    
    while left <= right:
        mid = (left + right) // 2
        
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    
    return left

115. Search a 2D Matrix

Описание: Поиск элемента в отсортированной по строкам и столбцам матрице.

def searchMatrix(matrix, target):
    """
    Поиск элемента в отсортированной матрице
    """
    if not matrix or not matrix[0]:
        return False
    
    rows, cols = len(matrix), len(matrix[0])
    
    # Двоичный поиск, рассматривая матрицу как одномерный массив
    left, right = 0, rows * cols - 1
    
    while left <= right:
        mid = (left + right) // 2
        # Преобразуем индекс в координаты
        row = mid // cols
        col = mid % cols
        
        if matrix[row][col] == target:
            return True
        elif matrix[row][col] < target:
            left = mid + 1
        else:
            right = mid - 1
    
    return False

116. Find Peak Element

Описание: Найти любой пиковый элемент в массиве.

def findPeakElement(nums):
    """
    Поиск пикового элемента в массиве
    """
    left, right = 0, len(nums) - 1
    
    while left < right:
        mid = (left + right) // 2
        
        if nums[mid] > nums[mid + 1]:
            # Пик слева или в середине
            right = mid
        else:
            # Пик справа
            left = mid + 1
    
    return left

117. Search in Rotated Sorted Array

Описание: Поиск элемента в отсортированном, но сдвинутом массиве.

def search(nums, target):
    """
    Поиск в отсортированном, но сдвинутом массиве
    """
    left, right = 0, len(nums) - 1
    
    while left <= right:
        mid = (left + right) // 2
        
        if nums[mid] == target:
            return mid
        
        # Определяем, какая половина отсортирована
        if nums[left] <= nums[mid]:
            # Левая половина отсортирована
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        else:
            # Правая половина отсортирована
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    
    return -1

118. Find First and Last Position of Element in Sorted Array

Описание: Найти первое и последнее вхождение элемента в отсортированном массиве.

def searchRange(nums, target):
    """
    Поиск первого и последнего вхождения элемента
    """
    def find_left(nums, target):
        """Находит первое вхождение"""
        left, right = 0, len(nums) - 1
        first = -1
        
        while left <= right:
            mid = (left + right) // 2
            
            if nums[mid] >= target:
                right = mid - 1
                if nums[mid] == target:
                    first = mid
            else:
                left = mid + 1
        
        return first
    
    def find_right(nums, target):
        """Находит последнее вхождение"""
        left, right = 0, len(nums) - 1
        last = -1
        
        while left <= right:
            mid = (left + right) // 2
            
            if nums[mid] <= target:
                left = mid + 1
                if nums[mid] == target:
                    last = mid
            else:
                right = mid - 1
        
        return last
    
    first = find_left(nums, target)
    last = find_right(nums, target)
    
    return [first, last]

119. Find Minimum in Rotated Sorted Array

Описание: Найти минимальный элемент в отсортированном, но сдвинутом массиве.

def findMin(nums):
    """
    Поиск минимального элемента в сдвинутом массиве
    """
    left, right = 0, len(nums) - 1
    
    # Массив не сдвинут
    if nums[left] < nums[right]:
        return nums[left]
    
    while left < right:
        mid = (left + right) // 2
        
        if nums[mid] > nums[right]:
            # Минимум справа от mid
            left = mid + 1
        else:
            # Минимум слева от mid или в mid
            right = mid
    
    return nums[left]

120. Median of Two Sorted Arrays

Описание: Найти медиану двух отсортированных массивов за O(log(min(n,m))).

def findMedianSortedArrays(nums1, nums2):
    """
    Поиск медианы двух отсортированных массивов
    """
    # Убедимся, что nums1 - меньший массив
    if len(nums1) > len(nums2):
        nums1, nums2 = nums2, nums1
    
    m, n = len(nums1), len(nums2)
    total = m + n
    half = total // 2
    
    left, right = 0, m
    
    while left <= right:
        # Разбиваем nums1
        i = (left + right) // 2
        # Разбиваем nums2
        j = half - i
        
        # Получаем граничные элементы
        nums1_left = nums1[i-1] if i > 0 else float('-inf')
        nums1_right = nums1[i] if i < m else float('inf')
        nums2_left = nums2[j-1] if j > 0 else float('-inf')
        nums2_right = nums2[j] if j < n else float('inf')
        
        # Проверяем правильность разбиения
        if nums1_left <= nums2_right and nums2_left <= nums1_right:
            # Правильное разбиение
            if total % 2 == 1:
                # Нечетное количество элементов
                return min(nums1_right, nums2_right)
            else:
                # Четное количество элементов
                return (max(nums1_left, nums2_left) + min(nums1_right, nums2_right)) / 2
        elif nums1_left > nums2_right:
            # Слишком много элементов из nums1 в левой части
            right = i - 1
        else:
            # Слишком мало элементов из nums1 в левой части
            left = i + 1
    
    return 0.0

Ключевые моменты четвертого блока:

  1. Графы и поиск (91-97): BFS, DFS, топологическая сортировка
  2. Trie и деревья (98-100): Префиксные деревья, поиск слов
  3. Backtracking (101-107): Комбинации, перестановки, поиск с возвратом
  4. Divide & Conquer (108-111): Разделяй и властвуй, сортировка слиянием
  5. Kadane's Algorithm (112-113): Максимальный подмассив
  6. Binary Search (114-120): Двоичный поиск в различных условиях

Основные алгоритмы:

  • Топологическая сортировка для задач расписания
  • Backtracking для комбинаторных задач
  • Merge Sort для связных списков
  • Алгоритм Кадане для максимального подмассива
  • Двоичный поиск в различных вариациях