Алгоритмы и структуры данных на Swift: от основ до продвинутых техник
Понимание алгоритмов и структур данных — фундамент, отличающий начинающего разработчика от опытного инженера. Для iOS/macOS-разработчика, работающего на Swift, это знание напрямую влияет на производительность приложения, время отклика интерфейса и эффективность использования памяти. В этой статье мы разберём ключевые структуры данных и алгоритмы, которые необходимо знать каждому Swift-разработчику, с практическими примерами кода.
1. Базовые структуры данных в Swift: массивы, множества и словари
Swift предоставляет мощные встроенные коллекции, но понимание их внутреннего устройства помогает писать более эффективный код.
Массивы (Array)
Массив — упорядоченная коллекция элементов с доступом по индексу. В Swift Array реализован как динамический массив с амортизированной сложностью O(1) для добавления в конец.
// Создание и базовые операции
var numbers = [1, 2, 3, 4, 5]
numbers.append(6) // O(1) в среднем
numbers.insert(0, at: 0) // O(n) — сдвиг элементов
let first = numbers[0] // O(1)
numbers.removeLast() // O(1)
numbers.remove(at: 2) // O(n) — сдвиг
Множества (Set)
Set — неупорядоченная коллекция уникальных элементов. Реализована на основе хеш-таблицы, что даёт O(1) для вставки, удаления и поиска.
var uniqueNumbers: Set<Int> = [1, 2, 3, 3, 4]
print(uniqueNumbers) // [1, 2, 3, 4] — дубликаты удалены
uniqueNumbers.insert(5) // O(1)
let contains = uniqueNumbers.contains(3) // true, O(1)
Словари (Dictionary)
Ассоциативный массив для хранения пар "ключ-значение". Также основан на хеш-таблицах.
var scores: [String: Int] = ["Alice": 95, "Bob": 87]
scores["Charlie"] = 92 // O(1)
if let aliceScore = scores["Alice"] {
print("Alice: \(aliceScore)")
}
scores.removeValue(forKey: "Bob") // O(1)
2. Пользовательские структуры данных: стеки, очереди и связанные списки
Хотя в Swift нет встроенных реализаций стека и очереди, их легко реализовать самостоятельно.
Стек (Stack) — LIFO
Стек работает по принципу "последний вошёл — первый вышел". Используется для отмены действий, обхода деревьев в глубину.
struct Stack<Element> {
private var storage: [Element] = []
mutating func push(_ element: Element) { storage.append(element) }
mutating func pop() -> Element? { storage.popLast() }
func peek() -> Element? { storage.last }
var isEmpty: Bool { storage.isEmpty }
}
var stack = Stack<Int>()
stack.push(1); stack.push(2); stack.push(3)
print(stack.pop()!) // 3
print(stack.peek()!) // 2
Очередь (Queue) — FIFO
Очередь работает по принципу "первый вошёл — первый вышел". Используется для обработки задач в порядке поступления.
struct Queue<Element> {
private var input: [Element] = []
private var output: [Element] = []
mutating func enqueue(_ element: Element) { input.append(element) }
mutating func dequeue() -> Element? {
if output.isEmpty {
output = input.reversed()
input.removeAll()
}
return output.popLast()
}
var isEmpty: Bool { input.isEmpty && output.isEmpty }
}
var queue = Queue<String>()
queue.enqueue("First"); queue.enqueue("Second")
print(queue.dequeue()!) // "First"
Связанный список (Linked List)
Связанный список состоит из узлов, каждый из которых ссылается на следующий. Позволяет эффективно вставлять/удалять элементы в середине списка.
class ListNode<T> {
var value: T
var next: ListNode?
init(_ value: T) { self.value = value }
}
class LinkedList<T> {
var head: ListNode<T>?
func append(_ value: T) {
let newNode = ListNode(value)
guard var current = head else { head = newNode; return }
while let next = current.next { current = next }
current.next = newNode
}
func remove(at index: Int) {
guard index > 0, let _ = head else { return }
var current = head
var prev: ListNode<T>?
for _ in 0..<index {
prev = current
current = current?.next
}
prev?.next = current?.next
}
}
3. Алгоритмы сортировки и поиска на Swift
Быстрая сортировка (Quick Sort)
Один из самых эффективных алгоритмов сортировки со средней сложностью O(n log n).
func quickSort<T: Comparable>(_ array: [T]) -> [T] {
guard array.count > 1 else { return array }
let pivot = array[array.count / 2]
let less = array.filter { $0 < pivot }
let equal = array.filter { $0 == pivot }
let greater = array.filter { $0 > pivot }
return quickSort(less) + equal + quickSort(greater)
}
let unsorted = [3, 6, 1, 8, 2, 5]
let sorted = quickSort(unsorted) // [1, 2, 3, 5, 6, 8]
Бинарный поиск (Binary Search)
Работает только на отсортированных массивах. Сложность O(log n).
func binarySearch<T: Comparable>(_ array: [T], target: T) -> Int? {
var low = 0
var high = array.count - 1
while low <= high {
let mid = (low + high) / 2
if array[mid] == target { return mid }
if array[mid] < target { low = mid + 1 }
else { high = mid - 1 }
}
return nil
}
let sortedArray = [1, 3, 5, 7, 9, 11]
if let index = binarySearch(sortedArray, target: 7) {
print("Found at index \(index)") // Found at index 3
}
4. Деревья и графы в Swift
Бинарное дерево поиска (BST)
Структура, где каждый узел имеет левого и правого потомка. Поиск, в