Swift on LeetCode: Where to Start and How Not to Give Up
Every iOS developer eventually hits a wall: you build projects, submit apps to the AppStore, but during an interview you get a problem about a binary tree and freeze up. Sound familiar? LeetCode is the training ground that transforms "I just write UI" into "I think in algorithms." But there's a catch: most solutions online are written in Python or Java, while Swift is a language with its own philosophy. In this article, we'll break down how to effectively solve LeetCode problems specifically in Swift, which data structures to use, and what to pay attention to so you don't waste time.
This article is for those who already know the basics of Swift but want to improve their algorithmic thinking. We won't be teaching syntax — we'll be learning how to think.
Why Swift is an Excellent Choice for LeetCode
Many people think Swift is only UIKit and SwiftUI, but the language actually has powerful capabilities for algorithmic problems:
- Optional types — safe handling of nil without unexpected crashes.
- Value types (structs) — predictable copying, no surprises with references.
- Lazy evaluation and functional approaches — map, filter, reduce greatly simplify code.
- Speed — Swift compiles to native code, often outperforming Python by 2-3 times.
However, there are pitfalls: for example, arrays in Swift are structs, and copying a large array can be expensive. But we'll discuss that separately.
Data Structures: What You Need to Know for Swift on LeetCode
Before diving into solving problems, make sure you are comfortable working with basic structures. Here's a must-know list for Swift:
Arrays and Slices (Array, ArraySlice)
Arrays in Swift are not just lists, but powerful structures with O(1) index access. But be careful: insertion/deletion at the beginning is O(n). Use ArraySlice to avoid copying data when working with subarrays.
let numbers = [1, 2, 3, 4, 5]let slice = numbers[1..<4] // ArraySlice, no copyingprint(slice) // [2, 3, 4]Dictionaries and Sets (Dictionary, Set)
For problems involving frequency counting, duplicate detection, or two-pass algorithms — these are your best friends. Remember: Dictionary is a hash table, O(1) on average, but can degrade to O(n) with a poor hash function.
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 and Queue
Swift doesn't have built-in stack and queue structures, but they are easy to emulate with an array. For a stack — use append and popLast. For a queue, use ArraySlice or a doubly linked list to avoid O(n) dequeue operations.
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 Lists
LeetCode loves problems involving singly linked lists. Swift doesn't have a built-in implementation, but you can define a class:
class ListNode { var val: Int var next: ListNode? init(_ val: Int) { self.val = val; self.next = nil }}This is classic for problems involving list reversal, cycle detection, or node deletion.
Algorithmic Patterns for Swift Problems
To avoid reinventing the wheel every time, remember a few patterns that cover 80% of LeetCode problems.
Two Pointers
An