Algorithms and Data Structures in Swift: From Basics to Advanced Techniques
Understanding algorithms and data structures is the foundation that distinguishes a beginner developer from an experienced engineer. For an iOS/macOS developer working in Swift, this knowledge directly impacts application performance, interface response time, and memory usage efficiency. In this article, we will cover key data structures and algorithms that every Swift developer needs to know, with practical code examples.
1. Basic Data Structures in Swift: Arrays, Sets, and Dictionaries
Swift provides powerful built-in collections, but understanding their internal workings helps write more efficient code.
Arrays (Array)
An array is an ordered collection of elements with index-based access. In Swift, Array is implemented as a dynamic array with amortized O(1) complexity for appending to the end.
// Creation and basic operationsvar numbers = [1, 2, 3, 4, 5]numbers.append(6) // O(1) on averagenumbers.insert(0, at: 0) // O(n) — element shiftinglet first = numbers[0] // O(1)numbers.removeLast() // O(1)numbers.remove(at: 2) // O(n) — shiftingSets (Set)
Set is an unordered collection of unique elements. It is implemented using a hash table, providing O(1) for insertion, deletion, and lookup.
var uniqueNumbers: Set<Int> = [1, 2, 3, 3, 4]print(uniqueNumbers) // [1, 2, 3, 4] — duplicates removeduniqueNumbers.insert(5) // O(1)let contains = uniqueNumbers.contains(3) // true, O(1)Dictionaries (Dictionary)
An associative array for storing key-value pairs. Also based on hash tables.
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. Custom Data Structures: Stacks, Queues, and Linked Lists
Although Swift does not have built-in implementations of stacks and queues, they are easy to implement yourself.
Stack (Stack) — LIFO
A stack works on the principle of "last in, first out". It is used for undo operations, depth-first tree traversal.
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()!) // 3print(stack.peek()!) // 2Queue (Queue) — FIFO
A queue works on the principle of "first in, first out". It is used for processing tasks in the order they arrive.
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 (Linked List)
A linked list consists of nodes, each referencing the next one. It allows efficient insertion/deletion of elements in the middle of the 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(_ valu