1. Set в Swift
Изучение данного блока предполагает предварительное знание синтаксиса языка Swift. Для успешного освоения этого материала, необходимо иметь базовое понимание синтаксиса языка Swift. Это включает в себя знание основных структур данных, операторов, циклов, функций, абстракций и других ключевых элементов языка. Без этих фундаментальных знаний будет сложно понять более сложные концепции и примеры, которые будут рассматриваться в данном блоке.
В Swift Set (множество) — это неупорядоченная коллекция уникальных значений одного типа. В отличие от массивов, где элементы могут повторяться и имеют определенный порядок, Set гарантирует, что каждый элемент присутствует только один раз, и не обеспечивает определенного порядка хранения.
Сеты используются, когда важно обеспечить уникальность элементов, быстро проверять наличие определенного значения и выполнять операции теории множеств, такие как объединение, пересечение и разность.
Синтаксис¶
Создание Set¶
Создание пустого Set с явным указанием типа¶
var emptySet = Set<Int>()
Создание пустого Set с использованием литерала массива¶
var anotherEmptySet: Set<String> = []
Основные операции¶
Добавление элементов¶
insert(_:)¶
Добавляет новый элемент в множество. Если элемент уже существует, он не будет добавлен повторно.
var colors: Set = ["Red", "Green"]
colors.insert("Blue")
print(colors) // ["Red", "Green", "Blue"]
Удаление элементов¶
remove(_:)¶
Удаляет указанный элемент из множества. Возвращает удаленный элемент, если он существовал, или nil в противном случае.
if let removedColor = colors.remove("Green") {
print("\(removedColor) был удален")
}
print(colors) // ["Red", "Blue"]
removeAll()¶
Удаляет все элементы из множества.
colors.removeAll()
print(colors) // Пустое множество
Проверка на наличие элемента/элементов¶
contains(_:)¶
let cities: Set = ["Moscow", "London", "New York"]
if cities.contains("London") {
print("London есть в множестве городов")
}
count¶
Свойство, возвращающее количество элементов в множестве.
print("Количество городов: \(cities.count)") // Количество городов: 3
isEmpty¶
Свойство, возвращающее true, если множество пусто.
let emptySet = Set<Int>()
if emptySet.isEmpty {
print("Множество пусто")
}
Итерация по элементам Set¶
Поскольку Set соответствует протоколу Sequence, вы можете использовать цикл for-in для перебора элементов.
for city in cities {
print(city)
}
Сортировка элементов¶
Чтобы получить отсортированный список элементов из множества, используйте метод sorted(), который возвращает массив отсортированных элементов.
let numbers: Set = [3, 1, 2]
let sortedNumbers = numbers.sorted()
print(sortedNumbers) // [1, 2, 3]
Операции над множествами¶
Объединение (Union)¶
union(_:)¶
Возвращает новое множество, содержащее все уникальные элементы обоих множеств.
let setA: Set = [1, 2, 3]
let setB: Set = [3, 4, 5]
let unionSet = setA.union(setB)
print(unionSet) // [1, 2, 3, 4, 5]
formUnion(_:)¶
Модифицирует текущее множество, добавляя в него элементы из другого множества.
var setC: Set = [1, 2]
setC.formUnion([2, 3, 4])
print(setC) // [1, 2, 3, 4]
Пересечение (Intersection)¶
intersection(_:)¶
Возвращает новое множество, содержащее общие элементы обоих множеств.
let intersectionSet = setA.intersection(setB)
print(intersectionSet) // [3]
formIntersection(_:)¶
Модифицирует текущее множество, оставляя в нем только общие элементы с другим множеством.
var setD: Set = [1, 2, 3]
setD.formIntersection([2, 3, 4])
print(setD) // [2, 3]
Разность (Subtracting)¶
subtracting(_:)¶
Возвращает новое множество, содержащее элементы текущего множества, которые не содержатся в указанном множестве.
let subtractingSet = setA.subtracting(setB)
print(subtractingSet) // [1, 2]
subtract(_:)¶
Модифицирует текущее множество, удаляя из него элементы другого множества.
var setE: Set = [1, 2, 3, 4]
setE.subtract([3, 4])
print(setE) // [1, 2]
Симметрическая разность (Symmetric Difference)¶
symmetricDifference(_:)¶
Возвращает новое множество, содержащее элементы, которые есть в одном из множеств, но не в обоих.
let symmetricSet = setA.symmetricDifference(setB)
print(symmetricSet) // [1, 2, 4, 5]
formSymmetricDifference(_:)¶
Модифицирует текущее множество, заменяя его симметрической разностью с другим множеством.
var setF: Set = [1, 2, 3]
setF.formSymmetricDifference([3, 4, 5])
print(setF) // [1, 2, 4, 5]
Сравнение множеств¶
Проверка на подмножество и надмножество¶
isSubset(of:)¶
Проверяет, является ли текущее множество подмножеством другого множества.
let smallSet: Set = [1, 2]
let largeSet: Set = [1, 2, 3, 4]
print(smallSet.isSubset(of: largeSet)) // true
isSuperset(of:)¶
Проверяет, является ли текущее множество надмножеством другого множества.
print(largeSet.isSuperset(of: smallSet)) // true
isStrictSubset(of:) и isStrictSuperset(of:)¶
Проверяют строгие отношения подмножества и надмножества (исключая равенство множеств).
Проверка на пересечение и равенство¶
isDisjoint(with:)¶
Проверяет, не имеют ли множества общих элементов.
let setG: Set = [1, 2]
let setH: Set = [3, 4]
print(setG.isDisjoint(with: setH)) // true
==
Преобразование между Set и Array¶
Из Set в Array
let numberSet: Set = [1, 2, 3]
let numberArray = Array(numberSet)
print(numberArray) // Может быть [2, 3, 1] — порядок не гарантируется
let numberArray = [1, 2, 2, 3, 3, 3]
let numberSet = Set(numberArray)
print(numberSet) // [1, 2, 3]
Алгоритмическая сложность основных операций¶
Добавление элементов¶
insert(_:)¶
Амортизированная O(1)
Операция вставки элемента в Set реализована с использованием хеш-таблицы. В среднем, добавление элемента выполняется за константное время. Однако в редких случаях, когда происходит рехеширование (перераспределение хеш-таблицы при превышении определенного коэффициента загрузки), операция может занять больше времени, но это компенсируется в амортизированном анализе.
Удаление элементов¶
remove(_:)¶
Амортизированная O(1)
Удаление всех элементов требует обхода всей структуры данных и освобождения памяти, поэтому сложность линейна по количеству элементов n.
Проверка на наличие элемента/элементов¶
contains(_:)¶
O(1)
Проверка наличия элемента в Set выполняется за константное время благодаря хеш-таблице. По хеш-значению сразу определяется, есть ли элемент в множестве.
count¶
O(1)
Свойство count хранится и обновляется при каждой операции добавления или удаления элемента, поэтому доступ к нему выполняется за константное время.
isEmpty¶
O(1)
Проверка на пустоту — это простая проверка значения свойства count, что выполняется за константное время.
Итерация по элементам¶
Перебор элементов с помощью цикла for-in¶
O(n)
Для перебора всех элементов множества необходимо пройти по каждому из n элементов, что требует линейного времени.
Сортировка элементов¶
sorted()¶
O(n log n)
Метод sorted() возвращает отсортированный массив элементов множества. Сортировка требует времени O(n log n), где n — количество элементов в множестве.
Операции над множествами¶
union(_:)¶
O(n + m)
Возвращает новое множество, содержащее все уникальные элементы из двух множеств. Необходимо пройти по каждому элементу обоих множеств (размеры n и m) и добавить их в новое множество, что требует O(n + m) времени.
formUnion(_:)¶
O(m)
Модифицирует текущее множество, добавляя в него элементы из другого множества размером m. Каждая вставка выполняется за амортизированное O(1), итого O(m).
Пересечение (Intersection)¶
intersection(_:)¶
O(n)
Возвращает новое множество с общими элементами. Необходимо пройти по каждому элементу текущего множества (размер n) и проверить, содержится ли он в другом множестве (проверка за O(1)). Итого O(n).
formIntersection(_:)¶
O(n)
Модифицирует текущее множество, оставляя только общие элементы. Аналогично предыдущему, требуется пройти по всем элементам текущего множества.
Разность (Subtracting)¶
subtracting(_:)¶
O(n)
Возвращает новое множество с элементами, которые есть в текущем множестве, но отсутствуют в другом. Проход по каждому элементу текущего множества с проверкой наличия в другом множестве за O(1). Итого O(n).
subtract(_:)¶
O(m)
Модифицирует текущее множество, удаляя из него элементы другого множества размера m. Для каждого элемента из другого множества выполняется удаление за O(1), итого O(m).
Симметрическая разность (Symmetric Difference)¶
symmetricDifference(_:)¶
O(n + m)
Возвращает новое множество с элементами, которые присутствуют в одном из множеств, но не в обоих. Требуется пройти по элементам обоих множеств и выполнить операции вставки и удаления, итого O(n + m).
Сравнение множеств¶
Проверка на подмножество и надмножество¶
isSubset(of:)¶
O(n)
Проверяет, являются ли все элементы текущего множества (размер n) элементами другого множества. Проходит по каждому элементу текущего множества и проверяет наличие в другом множестве за O(1), итого O(n).
isSuperset(of:)¶
O(m)
Проверяет, содержит ли текущее множество все элементы другого множества размера m. Проходит по каждому элементу другого множества и проверяет наличие в текущем множестве за O(1), итого O(m).
isStrictSubset(of:), isStrictSuperset(of:)¶
O(n) или O(m)
Аналогично предыдущим методам с дополнительной проверкой на неравенство размеров множеств.
Проверка на пересечение и равенство¶
isDisjoint(with:) или ==¶
O(n)
Проверяет, не имеют ли множества общих элементов. Проходит по каждому элементу текущего множества и проверяет наличие в другом множестве за O(1), останавливается при нахождении общего элемента. В худшем случае — O(n).
Итоговая таблица¶
| Операция | Метод | Сложность | Объяснение |
|---|---|---|---|
| Добавление элемента | insert(_:) |
Амортизированная O(1) | Добавление элемента в Set реализовано через хеш-таблицу. В среднем операция выполняется за O(1), хотя в редких случаях может потребоваться больше времени из-за рехеширования. |
| Удаление элемента | remove(_:) |
Амортизированная O(1) | Удаление элемента также использует хеш-таблицу для быстрого доступа. Амортизированная сложность составляет O(1), с учетом возможного рехеширования. |
| Удаление всех элементов | removeAll() |
O(n) | Требуется пройти по всей структуре данных для освобождения памяти и удаления элементов, поэтому сложность линейна от количества элементов n. |
| Проверка наличия элемента | contains(_:) |
O(1) | Используя хеш-таблицу, проверка наличия элемента выполняется за константное время, обращаясь напрямую по хешу. |
| Получение количества элементов | count |
O(1) | Свойство count хранится и обновляется при каждой модификации множества, поэтому доступ к нему занимает константное время. |
| Проверка на пустоту | isEmpty |
O(1) | Проверка выполняется через сравнение свойства count с нулем, что занимает константное время. |
| Итерация по элементам | Цикл for-in |
O(n) | Необходимо пройти по каждому из n элементов множества, что требует линейного времени. Порядок элементов не гарантируется. |
| Сортировка элементов | sorted() |
O(n log n) | Сортировка требует сравнения элементов и организации их в порядке, что занимает O(n log n) времени. Возвращает отсортированный массив. |
| Объединение множеств | union(_:) |
O(n + m) | Создает новое множество, проходя по элементам обоих множеств размером n и m соответственно. Каждая вставка в новое множество занимает O(1). |
| Модификация при объединении | formUnion(_:) |
O(m) | Добавляет элементы из другого множества размера m в текущее множество. Каждая операция вставки выполняется за O(1). |
| Пересечение множеств | intersection(_:) |
O(n) | Проходит по элементам текущего множества размера n и проверяет наличие каждого в другом множестве за O(1). |
| Модификация при пересечении | formIntersection(_:) |
O(n) | Удаляет из текущего множества элементы, отсутствующие в другом. Проход по n элементам с проверкой за O(1). |
| Разность множеств | subtracting(_:) |
O(n) | Проходит по элементам текущего множества и исключает те, что присутствуют в другом множестве. Проверка присутствия за O(1). |
| Модификация при разности | subtract(_:) |
O(m) | Удаляет из текущего множества элементы другого множества размера m. Каждое удаление выполняется за O(1). |
| Симметрическая разность | symmetricDifference(_:) |
O(n + m) | Создает новое множество, включая элементы, присутствующие только в одном из множеств. Требуется пройти по обоим множествам. |
| Модификация при симметрической разности | formSymmetricDifference(_:) |
O(m) | Для каждого элемента из другого множества размера m проверяется присутствие и выполняется добавление или удаление за O(1). |
| Проверка на подмножество | isSubset(of:) |
O(n) | Проходит по каждому элементу текущего множества размера n и проверяет его наличие в другом множестве за O(1). |
| Проверка на надмножество | isSuperset(of:) |
O(m) | Проверяет, содержатся ли все элементы другого множества размера m в текущем. Проход по m элементам с проверкой за O(1). |
| Проверка на пересечение | isDisjoint(with:) |
O(n) | Проходит по элементам текущего множества и проверяет отсутствие каждого в другом множестве. Останавливается при первом совпадении. |
Примечания:
- n — размер текущего множества.
- m — размер другого множества или коллекции.
- Амортизированная сложность учитывает среднее время выполнения операций, сглаживая редкие случаи увеличения времени выполнения (например, при рехешировании).
- Операции с пользовательскими типами: Элементы должны соответствовать протоколу
Hashable. Операции хеширования и сравнения должны выполняться за O(1) для сохранения общей сложности операций с множеством.