Swift на LeetCode: как прокачать алгоритмы и решать задачи

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Swift на LeetCode: с чего начать и как не бросить

Каждый iOS-разработчик рано или поздно упирается в стену: проекты делаешь, приложения в AppStore отправляешь, а на собеседовании дают задачу про бинарное дерево, и ты впадаешь в ступор. Знакомо? LeetCode — это тот самый тренажёр, который превращает «я просто пишу UI» в «я мыслю алгоритмами». Но есть нюанс: большинство решений в интернете написаны на Python или Java, а Swift — язык со своей философией. В этой статье мы разберём, как эффективно решать задачи на LeetCode именно на Swift, какие структуры данных использовать и на что обратить внимание, чтобы не терять время.

Эта статья для тех, кто уже знает основы Swift, но хочет прокачать алгоритмическое мышление. Мы не будем учить синтаксис — мы будем учиться думать.

Почему Swift — отличный выбор для LeetCode

Многие думают, что Swift — это только UIKit и SwiftUI, но на самом деле язык обладает мощными возможностями для алгоритмических задач:

  • Опциональные типы — безопасная работа с nil без неожиданных крашей.
  • Value types (структуры) — предсказуемое копирование, никаких сюрпризов с ссылками.
  • Ленивые вычисления и функциональные подходы — map, filter, reduce сильно упрощают код.
  • Скорость — Swift компилируется в нативный код, часто обгоняя Python в 2-3 раза.

Однако есть и подводные камни: например, массивы в Swift — это структуры, и копирование большого массива может быть дорогим. Но об этом мы поговорим отдельно.

Структуры данных: что нужно знать для Swift на LeetCode

Прежде чем кидаться решать задачи, убедитесь, что вы уверенно работаете с базовыми структурами. Вот список must-know для Swift:

Массивы и срезы (Array, ArraySlice)

Массивы в Swift — это не просто списки, а мощные структуры с O(1) доступом по индексу. Но будьте аккуратны: вставка/удаление в начале — O(n). Используйте ArraySlice, чтобы не копировать данные при работе с подмассивами.

let numbers = [1, 2, 3, 4, 5]
let slice = numbers[1..<4] // ArraySlice, без копирования
print(slice) // [2, 3, 4]

Словари и множества (Dictionary, Set)

Для задач на подсчёт частот, поиск дубликатов или двухпроходные алгоритмы — это ваши лучшие друзья. Помните: Dictionary — это хеш-таблица, O(1) в среднем, но может деградировать до O(n) при плохой хеш-функции.

func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
    var dict = [Int: Int]()
    for (i, num) in nums.enumerated() {
        if let j = dict[target - num] {
            return [j, i]
        }
        dict[num] = i
    }
    return []
}

Стек и очередь (Stack, Queue)

В Swift нет встроенных структур стека и очереди, но их легко эмулировать массивом. Для стека — append и popLast. Для очереди используйте ArraySlice или двусвязный список, чтобы избежать O(n) при dequeue.

struct Stack<T> {
    private var array = [T]()
    mutating func push(_ element: T) { array.append(element) }
    mutating func pop() -> T? { array.popLast() }
    var isEmpty: Bool { array.isEmpty }
}

Связные списки (Linked List)

LeetCode любит задачи на односвязные списки. В Swift нет встроенной реализации, но вы можете определить класс:

class ListNode {
    var val: Int
    var next: ListNode?
    init(_ val: Int) { self.val = val; self.next = nil }
}

Это классика для задач на разворот списка, обнаружение цикла или удаление узла.

Алгоритмические паттерны для Swift-задач

Чтобы не изобретать велосипед каждый раз, запомните несколько шаблонов, которые покрывают 80% задач на LeetCode.

Два указателя (Two Pointers)

Идеально для отсортированных массивов. Один указатель в начале, другой в конце — двигаем их навстречу друг другу. Пример: задача «Container With Most Water».

func maxArea(_ height: [Int]) -> Int {
    var left = 0, right = height.count - 1
    var maxWater = 0
    while left < right {
        let area = min(height[left], height[right]) * (right - left)
        maxWater = max(maxWater, area)
        if height[left] < height[right] {
            left += 1
        } else {
            right -= 1
        }
    }
    return maxWater
}

Скользящее окно (Sliding Window)

Когда нужно найти подстроку или подмассив с определёнными свойствами. Используйте два указателя, которые двигаются в одном направлении, расширяя и сужая окно.

func lengthOfLongestSubstring(_ s: String) -> Int {
    var charSet = Set()
    var left = 0, maxLength = 0
    let chars = Array(s)
    for right in 0..<chars.count {
        while charSet.contains(chars[right]) {
            charSet.remove(chars[left])
            left += 1
        }
        charSet.insert(chars[right])
        maxLength = max(maxLength, right - left + 1)
    }
    return maxLength
}

Бинарный поиск (Binary Search)

Классика для отсортированных массивов. В Swift есть встроенный binarySearch для диапазонов, но на LeetCode часто нужно писать вручную.

func searchInsert(_ nums: [Int], _ target: Int) -> Int {
    var left = 0, right = nums.count - 1
    while left <= right {
        let mid = left + (right - left) / 2
        if nums[mid] == target { return mid }
        if nums[mid] < target { left = mid + 1 }
        else { right = mid - 1 }
    }
    return left
}

DFS и BFS для графов и деревьев

Swift отлично подходит для рекурсивных решений, но помните про стек вызовов: глубокая рекурсия может привести к stack overflow. Используйте итеративные подходы с явным стеком или очередью.

func inorderTraversal(_ root: TreeNode?) -> [Int] {
    var result = [Int]()
    var stack = [TreeNode]()
    var current = root
    while current != nil || !stack.isEmpty {
        while let node = current {
            stack.append(node)
            current = node.left
        }
        current = stack.removeLast()
        result.append(current!.val)
        current = current?.right
    }
    return result
}

Типичные ошибки новичков в Swift на LeetCode

Даже опытные iOS-разработчики делают эти ошибки, когда переходят на алгоритмические задачи:

  • Забывают про опциональные типы — работа с TreeNode? или ListNode?

Блоги

Рекомендаций по книгам