Start learning free

SWIFT · BIG O NOTATION

Time Complexity Guidelines in Swift, explained

What O(1) means

An operation runs in constant time, written O(1), when the work it does doesn't depend on the size of the input. Reading one element from an array of ten items and reading one from an array of ten million are the same amount of work. The name doesn't mean the operation is fast in absolute terms, only that it doesn't slow down as the data grows.

Constant-time operations on Swift arrays

var a = Array(1...10)
print(a.count)   // 10
print(a.first!)  // 1
print(a.last!)   // 10
print(a[5])      // 6
a.append(11)
print(a.removeLast())  // 11
  • Reading by index: a[5] goes straight to the position, because array elements are stored next to each other in memory.
  • Writing by index: a[5] = 0 replaces one element.
  • count, isEmpty, first and last: the array already knows its length and where its ends are.
  • append: amortized O(1). Usually it writes into free space at the end. Occasionally the array has to move to a bigger block of memory, which takes O(n). That growth is rare enough that the average cost per append stays constant.
  • removeLast(): takes the last element out without moving anything else.

Constant time elsewhere

let s: Set = [1, 2, 3]
print(s.contains(2))   // true

let d = ["a": 1]
print(d["a"] as Any)   // Optional(1)
print(d["z"] as Any)   // nil
  • Dictionary and Set lookups: contains on a Set and a dictionary subscript use hashing, so on average they are O(1), however many items are stored. A missing key gives nil for a dictionary subscript.
  • Arithmetic and comparisons: n % 2 == 0 and a + b are constant. They take the same time for any value that fits in an Int.
  • Variable assignment and function calls that only do the above.

Operations that look constant but are not

  • removeFirst() and insert(_:at: 0) on an array shift every other element, so they are O(n).
  • contains on an array scans the elements and is O(n). On a Set it is O(1) on average.
  • String.count is O(n), because Swift counts characters, and a character can take several bytes. For the string "héllo", count is 5 while utf8.count is 6.
  • Array indexing is by Int, but strings are not. A String can't be indexed with an integer for this reason; use Array(s) when you need random access.

Why arrays can jump straight to an index

An array stores its elements one after another in a single block of memory, and every element has the same size. The position of element i is then the start of the block plus i times the element size, one multiplication and one addition, no matter how large i is. A dictionary reaches a value differently: it turns the key into a number with a hash function, and that number says which slot to look at. Both approaches skip the scan through the other items, which is where the constant time comes from.

Counting constant work

A function whose body has no loops and calls only constant-time operations is O(1) overall:

func firstElement(_ nums: [Int]) -> Int? {
    return nums.first
}

A loop that always runs a fixed number of times, such as for i in 0..<3, is also constant, because the count doesn't depend on the input. A constant is not the same as small: a loop that always runs a million times is O(1), though it is slow.

Key takeaways

  • O(1) means the cost doesn't depend on the input size.
  • Array indexing, count, first, last, append (amortized) and removeLast are constant time.
  • Set and dictionary lookups are constant on average.
  • Operations that shift elements, scan, or count characters are linear.
Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
Start learning free