0% found this document useful (0 votes)
2 views22 pages

IOS Interview Problem Solving Guide

The document is a comprehensive iOS interview problem-solving guide covering essential topics in Swift, data structures, algorithms, and iOS patterns. It includes 30 problems categorized by difficulty levels, with full solutions and explanations for each problem, focusing on arrays, strings, searching, sorting, and more. The guide aims to prepare candidates for technical interviews by providing practical coding challenges and their solutions.

Uploaded by

asif0171797
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views22 pages

IOS Interview Problem Solving Guide

The document is a comprehensive iOS interview problem-solving guide covering essential topics in Swift, data structures, algorithms, and iOS patterns. It includes 30 problems categorized by difficulty levels, with full solutions and explanations for each problem, focusing on arrays, strings, searching, sorting, and more. The guide aims to prepare candidates for technical interviews by providing practical coding challenges and their solutions.

Uploaded by

asif0171797
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

iOS Interview

Problem Solving Guide


Swift • Data Structures • Algorithms • iOS Patterns

30 Essential Problems with Full Solutions & Explanations


Covering: Array • String • Search • Sort • Pattern • Stack • Linked List
Difficulty Legend & Index

🟢 Easy Basic array, string, number ops — must solve confidently

🟡 Medium Search, sort, patterns, logic — common in written tests

🟠 Medium-Hard Job written exams, LeetCode style — demonstrate depth

🔴 Hard Advanced DS concepts — differentiates senior candidates


🟢 Section 1: Basic Array Problems

Q1: Find Max & Min 🟢 Easy


Given an array of integers, find the maximum and minimum values.
Example:
Input: [3, 1, 9, 2, 7]
Output: Max = 9, Min = 1
Solution (Swift):
func findMaxMin(_ arr: [Int]) -> (max: Int, min: Int)? {
guard ![Link] else { return nil }
var maxVal = arr[0]
var minVal = arr[0]
for num in arr {
if num > maxVal { maxVal = num }
if num < minVal { minVal = num }
}
return (maxVal, minVal)
}

// Usage
if let result = findMaxMin([3, 1, 9, 2, 7]) {
print("Max: \([Link]), Min: \([Link])")
}
Explanation:
We initialize both max and min to the first element. Then we iterate once through the array comparing each element.
Time complexity: O(n), Space: O(1).
Array Iteration O(n)

Q2: Second Largest Number 🟢 Easy


Find the second largest element in an array. Handle duplicates.
Example:
Input: [3, 1, 9, 2, 7]
Output: 7

Input: [5, 5, 5]
Output: nil (no second largest)
Solution (Swift):
func secondLargest(_ arr: [Int]) -> Int? {
guard [Link] >= 2 else { return nil }
var first = [Link]
var second = [Link]
for num in arr {
if num > first {
second = first
first = num
} else if num > second && num != first {
second = num
}
}
return second == [Link] ? nil : second
}
Explanation:
Track two variables: `first` (largest) and `second`. When we find a new largest, demote first to second. The condition
`num != first` handles duplicates. Time: O(n), Space: O(1).
Array Two Variables Edge Cases

Q3: Count Occurrences 🟢 Easy


Count how many times a specific number appears in an array.
Example:
Input: arr = [1, 2, 3, 2, 4, 2], target = 2
Output: 3
Solution (Swift):
func countOccurrences(_ arr: [Int], target: Int) -> Int {
var count = 0
for num in arr {
if num == target { count += 1 }
}
return count
}

// Swift-idiomatic version using filter


func countOccurrencesSwifty(_ arr: [Int], target: Int) -> Int {
return [Link] { $0 == target }.count
}

// Using Dictionary for frequency of all elements


func frequencyMap(_ arr: [Int]) -> [Int: Int] {
var freq = [Int: Int]()
for num in arr { freq[num, default: 0] += 1 }
return freq
}
Explanation:
Simple iteration with a counter. The `frequencyMap` variant builds a Dictionary for all elements at once — useful when
you need counts for multiple targets. Time: O(n), Space: O(1) for counting, O(n) for map.
Array Dictionary Frequency

Q4: Reverse Array 🟢 Easy


Reverse an array without using built-in reverse functions.
Example:
Input: [1, 2, 3, 4, 5]
Output: [5, 4, 3, 2, 1]
Solution (Swift):
func reverseArray(_ arr: [Int]) -> [Int] {
var arr = arr
var left = 0
var right = [Link] - 1
while left < right {
let temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
left += 1
right -= 1
}
return arr
}
Explanation:
Two-pointer technique: swap elements from both ends moving toward the center. We stop when left >= right. Time:
O(n/2) = O(n), Space: O(1) in-place.
Array Two Pointers In-place
🟢 Section 2: Number Problems

Q5: Prime Number Check 🟢 Easy


Check if a given number is prime. A prime number is only divisible by 1 and itself.
Example:
Input: 7 → Output: true
Input: 9 → Output: false
Input: 1 → Output: false
Solution (Swift):
func isPrime(_ n: Int) -> Bool {
if n <= 1 { return false }
if n <= 3 { return true }
if n % 2 == 0 || n % 3 == 0 { return false }
var i = 5
while i * i <= n {
if n % i == 0 || n % (i + 2) == 0 { return false }
i += 6
}
return true
}
Explanation:
Optimized: check divisibility up to √n only. Skip even numbers and multiples of 3 early. The i += 6 pattern exploits that
all primes > 3 are of form 6k±1. Time: O(√n).
Math Prime Optimization

Q6: Fibonacci Sequence 🟢 Easy


Print the first n Fibonacci numbers. Each number is the sum of the two preceding ones.
Example:
Input: n = 7
Output: [0, 1, 1, 2, 3, 5, 8]
Solution (Swift):
// Iterative - O(n) time, O(1) space
func fibonacci(_ n: Int) -> [Int] {
guard n > 0 else { return [] }
if n == 1 { return [0] }
var result = [0, 1]
for _ in 2..<n {
let next = result[[Link] - 1] + result[[Link] - 2]
[Link](next)
}
return result
}

// Recursive with memoization - O(n) time, O(n) space


func fibMemo(_ n: Int, memo: inout [Int: Int]) -> Int {
if n <= 1 { return n }
if let cached = memo[n] { return cached }
memo[n] = fibMemo(n - 1, memo: &memo) + fibMemo(n - 2, memo: &memo)
return memo[n]!
}
Explanation:
Iterative is preferred — O(n) time and O(1) space. Memoized recursion is O(n) space but great for interviews showing
dynamic programming knowledge.
Math Dynamic Programming Memoization

Q7: Palindrome Number 🟢 Easy


Check if a number reads the same forwards and backwards.
Example:
Input: 121 → Output: true
Input: -121 → Output: false
Input: 10 → Output: false
Solution (Swift):
func isPalindromeNumber(_ n: Int) -> Bool {
if n < 0 { return false } // Negative numbers can't be palindrome
if n != 0 && n % 10 == 0 { return false } // Last digit 0
var original = n
var reversed = 0
while original > reversed {
reversed = reversed * 10 + original % 10
original /= 10
}
// Handle odd-length numbers with original == reversed / 10
return original == reversed || original == reversed / 10
}
Explanation:
Reverse only half the number and compare. Stop when reversed >= original. This handles odd-length numbers (middle
digit doesn't matter). No string conversion needed. Time: O(log n).
Math No String Conversion Edge Cases
🟢 Section 3: String Problems

Q8: Reverse String 🟢 Easy


Reverse a string without using built-in reverse methods.
Example:
Input: "hello"
Output: "olleh"
Solution (Swift):
func reverseString(_ str: String) -> String {
var chars = Array(str)
var left = 0
var right = [Link] - 1
while left < right {
let temp = chars[left]
chars[left] = chars[right]
chars[right] = temp
left += 1
right -= 1
}
return String(chars)
}

// Alternative: manual build


func reverseStringAlt(_ str: String) -> String {
var result = ""
for char in str { result = String(char) + result }
return result
}
Explanation:
Convert String to Character array for O(1) index access. Use two-pointer swap — same as array reversal. In Swift,
String doesn't support integer subscript directly, so Array(str) is key.
String Two Pointers Character Array

Q9: Palindrome String 🟢 Easy


Check if a string is a palindrome. Consider case-insensitive comparison.
Example:
Input: "madam" → Output: true
Input: "racecar" → Output: true
Input: "hello" → Output: false
Solution (Swift):
func isPalindromeString(_ str: String) -> Bool {
let clean = [Link]().filter { $[Link] || $[Link] }
let chars = Array(clean)
var left = 0
var right = [Link] - 1
while left < right {
if chars[left] != chars[right] { return false }
left += 1
right -= 1
}
return true
}

// Bonus: Check valid palindrome ignoring non-alphanumeric


// e.g. "A man, a plan, a canal: Panama" -> true
Explanation:
Clean the string first (lowercase, filter non-alphanumeric) then use two pointers. This correctly handles phrases like 'A
man a plan a canal Panama'. Time: O(n), Space: O(n).
String Two Pointers Preprocessing

Q10: Count Vowels 🟢 Easy


Count the number of vowels (a, e, i, o, u) in a string.
Example:
Input: "Hello World"
Output: 3 (e, o, o)
Solution (Swift):
func countVowels(_ str: String) -> Int {
let vowels: Set<Character> = ["a","e","i","o","u",
"A","E","I","O","U"]
var count = 0
for char in str {
if [Link](char) { count += 1 }
}
return count
}

// Functional style
func countVowelsFunctional(_ str: String) -> Int {
let vowels = "aeiouAEIOU"
return [Link] { [Link]($0) }.count
}
Explanation:
Using a Set<Character> gives O(1) lookup for each character check. Avoid String contains() in a loop — it's O(n) per
call. Set membership is amortized O(1). Total: O(n).
String Set O(n)

Q11: Anagram Check 🟢 Easy


Check if two strings are anagrams of each other (same characters, different order).
Example:
Input: "listen", "silent" → Output: true
Input: "hello", "world" → Output: false
Solution (Swift):
func isAnagram(_ s1: String, _ s2: String) -> Bool {
guard [Link] == [Link] else { return false }
var freq = [Character: Int]()
for char in s1 { freq[char, default: 0] += 1 }
for char in s2 {
freq[char, default: 0] -= 1
if freq[char]! < 0 { return false }
}
return true
}

// Alternative: sort and compare


func isAnagramSort(_ s1: String, _ s2: String) -> Bool {
return [Link]() == [Link]()
}
Explanation:
Frequency map approach: increment for s1, decrement for s2. If any count goes negative, s2 has a character s1
doesn't. Time: O(n). Sort approach is O(n log n) but simpler to write.
String Dictionary Frequency Map
🟡 Section 4: Searching & Sorting

Q12: Linear Search 🟡 Medium


Search for a target value in an unsorted array. Return its index.
Example:
Input: arr = [5, 3, 8, 1, 9], target = 8
Output: 2

Input: target = 7
Output: -1
Solution (Swift):
func linearSearch(_ arr: [Int], target: Int) -> Int {
for (index, value) in [Link]() {
if value == target { return index }
}
return -1
}
Explanation:
Check each element one by one. Best case O(1) (first element), worst case O(n) (not found). Used when array is
unsorted or small.
Search O(n) Unsorted Array

Q13: Binary Search 🟡 Medium


Search in a sorted array using binary search. Return the index.
Example:
Input: arr = [1, 3, 5, 7, 9, 11], target = 7
Output: 3
Solution (Swift):
func binarySearch(_ arr: [Int], target: Int) -> Int {
var left = 0
var right = [Link] - 1
while left <= right {
let mid = left + (right - left) / 2 // Avoids integer overflow
if arr[mid] == target { return mid }
else if arr[mid] < target { left = mid + 1 }
else { right = mid - 1 }
}
return -1
}

// Recursive version
func binarySearchRecursive(_ arr: [Int], target: Int,
left: Int, right: Int) -> Int {
guard left <= right else { return -1 }
let mid = left + (right - left) / 2
if arr[mid] == target { return mid }
if arr[mid] < target { return binarySearchRecursive(arr, target: target, left: mid+1,
right: right) }
return binarySearchRecursive(arr, target: target, left: left, right: mid-1)
}
Explanation:
Halve the search space each iteration. Use `left + (right - left) / 2` instead of `(left + right) / 2` to prevent integer
overflow. Time: O(log n), Space: O(1) iterative, O(log n) recursive.
Search O(log n) Sorted Array Divide & Conquer

Q14: Bubble Sort 🟡 Medium


Sort an array using bubble sort. Repeatedly swap adjacent elements if out of order.
Example:
Input: [64, 34, 25, 12, 22]
Output: [12, 22, 25, 34, 64]
Solution (Swift):
func bubbleSort(_ arr: [Int]) -> [Int] {
var arr = arr
let n = [Link]
for i in 0..<n {
var swapped = false
for j in 0..<(n - i - 1) {
if arr[j] > arr[j + 1] {
[Link](j, j + 1)
swapped = true
}
}
if !swapped { break } // Optimization: already sorted
}
return arr
}
Explanation:
Each pass bubbles the largest unsorted element to its correct position. The `swapped` flag is an optimization — if no
swaps occurred, array is sorted. Best: O(n), Average/Worst: O(n²).
Sorting O(n²) Swap In-place

Q15: Selection Sort 🟡 Medium


Find the minimum element and place it at the beginning. Repeat for remaining array.
Example:
Input: [29, 10, 14, 37, 13]
Output: [10, 13, 14, 29, 37]
Solution (Swift):
func selectionSort(_ arr: [Int]) -> [Int] {
var arr = arr
let n = [Link]
for i in 0..<n {
var minIndex = i
for j in (i + 1)..<n {
if arr[j] < arr[minIndex] { minIndex = j }
}
if minIndex != i { [Link](i, minIndex) }
}
return arr
}
Explanation:
Find the minimum in the unsorted portion, swap it to the front. Makes exactly n-1 swaps — useful when swaps are
expensive. Time: O(n²) always, Space: O(1).
Sorting O(n²) Minimum Finding
🟡 Section 5: Pattern Printing

Q16: Right Triangle Star Pattern 🟡 Medium


Print a right-angle triangle using stars.
Example:
*
**
***
****
*****
Solution (Swift):
func rightTriangle(_ rows: Int) {
for i in 1...rows {
print(String(repeating: "*", count: i))
}
}

// Number pattern variant


func numberTriangle(_ rows: Int) {
for i in 1...rows {
let row = (1...i).map { String($0) }.joined(separator: " ")
print(row)
}
}
// Output:
// 1
// 1 2
// 1 2 3
Explanation:
Outer loop controls rows; inner loop (or String(repeating:)) controls columns. The key insight: on row i, print i
stars/numbers.
Pattern Nested Loops String Repeat

Q17: Pyramid / Diamond Pattern 🟡 Medium


Print a centered pyramid pattern with stars.
Example:
*
***
*****
*******
Solution (Swift):
func pyramid(_ rows: Int) {
for i in 1...rows {
let spaces = String(repeating: " ", count: rows - i)
let stars = String(repeating: "*", count: 2 * i - 1)
print(spaces + stars)
}
}

func diamond(_ rows: Int) {


// Upper half (including middle)
for i in 1...rows {
let spaces = String(repeating: " ", count: rows - i)
let stars = String(repeating: "*", count: 2 * i - 1)
print(spaces + stars)
}
// Lower half
for i in stride(from: rows - 1, through: 1, by: -1) {
let spaces = String(repeating: " ", count: rows - i)
let stars = String(repeating: "*", count: 2 * i - 1)
print(spaces + stars)
}
}
Explanation:
For row i: spaces = (rows - i), stars = (2*i - 1). This centers the pyramid. Diamond is pyramid + inverted pyramid (stride
downward).
Pattern Math Formula Stride
🟡 Section 6: Logical Problems

Q18: Swap Without Temp Variable 🟡 Medium


Swap two integer variables without using a third variable.
Example:
Input: a = 5, b = 3
Output: a = 3, b = 5
Solution (Swift):
// Method 1: Arithmetic
func swapArithmetic(_ a: inout Int, _ b: inout Int) {
a = a + b // a = 8
b = a - b // b = 5
a = a - b // a = 3
}

// Method 2: XOR (bitwise) - safer, no overflow risk


func swapXOR(_ a: inout Int, _ b: inout Int) {
a = a ^ b
b = a ^ b
a = a ^ b
}

// Swift tuple swap (cleanest)


func swapTuple(_ a: inout Int, _ b: inout Int) {
(a, b) = (b, a)
}
Explanation:
Three techniques: (1) Arithmetic: risk of overflow for large numbers. (2) XOR: bitwise, no overflow, works for integers.
(3) Tuple: the Swift-idiomatic way. XOR is the classic interview answer.
Logic XOR In-place inout

Q19: Find Duplicates in Array 🟡 Medium


Find all duplicate numbers in an array.
Example:
Input: [1, 2, 3, 2, 4, 3, 5]
Output: [2, 3]
Solution (Swift):
func findDuplicates(_ arr: [Int]) -> [Int] {
var seen = Set<Int>()
var duplicates = Set<Int>()
for num in arr {
if [Link](num) {
[Link](num)
} else {
[Link](num)
}
}
return Array(duplicates)
}

// Using Dictionary for count


func findDuplicatesWithCount(_ arr: [Int]) -> [Int: Int] {
var freq = [Int: Int]()
for num in arr { freq[num, default: 0] += 1 }
return [Link] { $[Link] > 1 }
}
Explanation:
Using a Set for O(1) lookup. First Set tracks seen elements, second Set tracks confirmed duplicates (avoids reporting
same duplicate multiple times). Time: O(n), Space: O(n).
Array Set Dictionary O(n)

Q20: Sum of Digits 🟡 Medium


Find the sum of all digits in a number.
Example:
Input: 123 → Output: 6 (1+2+3)
Input: 9875 → Output: 29
Solution (Swift):
func sumOfDigits(_ n: Int) -> Int {
var n = abs(n) // Handle negative numbers
var sum = 0
while n > 0 {
sum += n % 10 // Get last digit
n /= 10 // Remove last digit
}
return sum
}

// String approach (easier to read)


func sumOfDigitsString(_ n: Int) -> Int {
return String(abs(n)).compactMap { $[Link] }.reduce(0, +)
}
Explanation:
Modulo 10 extracts the last digit; integer division by 10 removes it. `abs()` handles negative input. The functional
approach is very readable for Swift interviews.
Math Modulo Digit Extraction
🟠 Section 7: Medium-Level — Job Written

Q21: Two Sum 🟠 Medium


Find two numbers in an array that add up to a target value. Return their indices.
Example:
Input: arr = [2, 7, 11, 15], target = 9
Output: [0, 1] (because 2 + 7 = 9)
Solution (Swift):
func twoSum(_ nums: [Int], target: Int) -> [Int] {
var map = [Int: Int]() // value -> index
for (i, num) in [Link]() {
let complement = target - num
if let j = map[complement] {
return [j, i]
}
map[num] = i
}
return []
}

// Brute force O(n²) - mention but prefer HashMap


func twoSumBrute(_ nums: [Int], target: Int) -> [Int] {
for i in 0..<[Link] {
for j in (i+1)..<[Link] {
if nums[i] + nums[j] == target { return [i, j] }
}
}
return []
}
Explanation:
HashMap approach: for each number, calculate its complement (target - num). If complement exists in map, we found
the pair. Store each number's index as we go. Time: O(n), Space: O(n). This is the classic LeetCode #1.
Array HashMap O(n) LeetCode Classic

Q22: Missing Number 🟠 Medium


Find the missing number from an array containing 0 to n with one number missing.
Example:
Input: [3, 0, 1] (n=3)
Output: 2
Solution (Swift):
// Method 1: Math formula O(n) time, O(1) space
func missingNumber(_ nums: [Int]) -> Int {
let n = [Link]
let expected = n * (n + 1) / 2 // Sum of 0..n
let actual = [Link](0, +)
return expected - actual
}

// Method 2: XOR approach


func missingNumberXOR(_ nums: [Int]) -> Int {
var xor = [Link]
for (i, num) in [Link]() {
xor ^= i ^ num
}
return xor
}
Explanation:
Gauss formula: expected sum of 0..n is n*(n+1)/2. Subtract actual sum to get the missing number. XOR approach: XOR
of a number with itself is 0, so XOR all indices and values cancels everything except the missing.
Math XOR Gauss Formula O(1) Space

Q23: Move Zeros to End 🟠 Medium


Move all zeros to the end of an array while maintaining relative order of non-zeros.
Example:
Input: [0, 1, 0, 3, 12]
Output: [1, 3, 12, 0, 0]
Solution (Swift):
func moveZeros(_ nums: inout [Int]) {
var insertPos = 0
// Move all non-zeros to front
for num in nums {
if num != 0 {
nums[insertPos] = num
insertPos += 1
}
}
// Fill remaining positions with zeros
while insertPos < [Link] {
nums[insertPos] = 0
insertPos += 1
}
}
Explanation:
Two-pass: first pass copies non-zero elements to the front using `insertPos` pointer. Second pass fills remaining slots
with zeros. In-place, preserves relative order. Time: O(n), Space: O(1).
Array Two Pointers In-place O(n)
🔴 Section 8: iOS-Specific Swift Problems

Q24: FizzBuzz 🟢 Easy


Print 1 to n. For multiples of 3 print 'Fizz', multiples of 5 print 'Buzz', multiples of both print 'FizzBuzz'.
Example:
Input: 15
Output: 1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz
Solution (Swift):
func fizzBuzz(_ n: Int) -> [String] {
return (1...n).map { i -> String in
switch (i % 3 == 0, i % 5 == 0) {
case (true, true): return "FizzBuzz"
case (true, false): return "Fizz"
case (false, true): return "Buzz"
default: return String(i)
}
}
}
Explanation:
Using a tuple switch on (divisible by 3, divisible by 5) makes the logic clear and exhaustive. The Swift functional map
approach is clean and interview-worthy.
Swift map Switch Tuple Modulo

Q25: Flatten Nested Array 🟠 Medium


Flatten an array of arrays into a single array.
Example:
Input: [[1, 2], [3, 4], [5]]
Output: [1, 2, 3, 4, 5]
Solution (Swift):
// Using flatMap
func flatten(_ arr: [[Int]]) -> [Int] {
return [Link] { $0 }
}

// Without built-ins
func flattenManual(_ arr: [[Int]]) -> [Int] {
var result = [Int]()
for subArr in arr {
for element in subArr {
[Link](element)
}
}
return result
}

// Recursive for deeply nested (using Any)


func flattenDeep(_ arr: [Any]) -> [Int] {
var result = [Int]()
for item in arr {
if let subArr = item as? [Any] {
[Link](contentsOf: flattenDeep(subArr))
} else if let num = item as? Int {
[Link](num)
}
}
return result
}
Explanation:
flatMap on an array of arrays is the Swift idiomatic approach. The manual version demonstrates nested loops. The
recursive approach handles arbitrary nesting depth using type casting.
Swift flatMap Recursion Higher-Order Functions

Q26: Group By / Dictionary Grouping 🟠 Medium


Group an array of words by their first letter.
Example:
Input: ["apple", "banana", "avocado", "blueberry", "cherry"]
Output: ["a": ["apple", "avocado"], "b": ["banana", "blueberry"], "c": ["cherry"]]
Solution (Swift):
func groupByFirstLetter(_ words: [String]) -> [Character: [String]] {
var groups = [Character: [String]]()
for word in words {
guard let first = [Link] else { continue }
groups[first, default: []].append(word)
}
return groups
}

// Using Dictionary(grouping:by:) — Swift standard lib


func groupByFirstLetterSwifty(_ words: [String]) -> [Character: [String]] {
return Dictionary(grouping: words) { $[Link]! }
}
Explanation:
`[key, default: []]` is a Swift pattern for building grouped dictionaries cleanly. `Dictionary(grouping:by:)` is the one-liner
solution. Both are O(n). Know both for interviews.
Swift Dictionary grouping Higher-Order

Q27: Longest Common Prefix 🟠 Medium


Find the longest common prefix string among an array of strings.
Example:
Input: ["flower", "flow", "flight"]
Output: "fl"

Input: ["dog", "racecar"]


Output: ""
Solution (Swift):
func longestCommonPrefix(_ strs: [String]) -> String {
guard ![Link] else { return "" }
var prefix = strs[0]
for str in [Link]() {
while ![Link](prefix) {
prefix = String([Link]())
if [Link] { return "" }
}
}
return prefix
}
Explanation:
Start with the first string as the prefix. For each subsequent string, shorten the prefix until it matches. In the worst case
we trim the entire prefix. Time: O(S) where S = total characters.
String Prefix Greedy
Q28: Valid Parentheses 🟠 Medium
Check if a string of brackets is valid (every open bracket has a matching close bracket in order).
Example:
Input: "()[]{}" → Output: true
Input: "([)]" → Output: false
Input: "{[]}" → Output: true
Solution (Swift):
func isValid(_ s: String) -> Bool {
var stack = [Character]()
let pairs: [Character: Character] = [")":"(", "]":"[", "}":"{"]
for char in s {
if "([{".contains(char) {
[Link](char)
} else if let open = pairs[char] {
if [Link] != open { return false }
[Link]()
}
}
return [Link]
}
Explanation:
Classic Stack problem. Push opening brackets. For closing brackets, check if top of stack is the matching opener. If
stack is empty at end, all brackets matched. Time: O(n), Space: O(n).
Stack Dictionary LeetCode Classic O(n)

Q29: Merge Two Sorted Arrays 🟠 Medium


Merge two sorted arrays into one sorted array without using sort().
Example:
Input: [1, 3, 5], [2, 4, 6]
Output: [1, 2, 3, 4, 5, 6]
Solution (Swift):
func mergeSorted(_ a: [Int], _ b: [Int]) -> [Int] {
var result = [Int]()
var i = 0, j = 0
while i < [Link] && j < [Link] {
if a[i] <= b[j] {
[Link](a[i])
i += 1
} else {
[Link](b[j])
j += 1
}
}
[Link](contentsOf: a[i...])
[Link](contentsOf: b[j...])
return result
}
Explanation:
Two-pointer merge: compare front elements of both arrays, append the smaller. After one array is exhausted, append
all remaining elements from the other. This is the merge step of merge sort. Time: O(n+m).
Array Two Pointers Merge Sort step O(n+m)

Q30: Linked List — Detect Cycle 🔴 Hard


Detect if a linked list has a cycle using Floyd's Tortoise and Hare algorithm.
Example:
1 -> 2 -> 3 -> 4 -> 2 (cycle back)
Output: true (cycle detected)
Solution (Swift):
class ListNode {
var val: Int
var next: ListNode?
init(_ val: Int) { [Link] = val }
}

func hasCycle(_ head: ListNode?) -> Bool {


var slow = head
var fast = head
while fast != nil && fast?.next != nil {
slow = slow?.next // Move 1 step
fast = fast?.next?.next // Move 2 steps
if slow === fast { return true } // === checks identity
}
return false
}
Explanation:
Floyd's algorithm: slow pointer moves 1 step, fast moves 2. If there's a cycle, they will eventually meet (like a lapped
runner on a track). Use `===` (identity) not `==` (value). Time: O(n), Space: O(1).
Linked List Two Pointers Floyd's Algorithm O(1) Space
💡 iOS Interview Tips

Before the Interview


Review Swift-specific syntax: optionals, guard, defer, closures, protocols, and generics.
Practice on a whiteboard or paper — you won't always have autocomplete.
Know Big O: be able to state time and space complexity for every solution.

During the Interview


1. Clarify: Ask about edge cases (empty array, negatives, duplicates) before coding.
2. Brute force first: State the naive solution and its complexity, then optimize.
3. Think aloud: Interviewers want to see your reasoning process.
4. Test your code: Trace through your example before declaring it done.

Swift-Specific Tips
Use inout parameters when modifying arrays/variables in place.
Prefer value semantics (struct) — know why Swift uses this by default.
Know the difference between === (identity) and == (equality).
Be comfortable with higher-order functions: map, filter, reduce, flatMap, compactMap.
Show optionals correctly: use guard let or if let, avoid force unwrapping (!) in interviews.

Big O Cheat Sheet


Algorithm Time Space
Linear Search O(n) O(1)

Binary Search O(log n) O(1)

Bubble/Selection Sort O(n²) O(1)

Merge Sort O(n log n) O(n)

Hash Map lookup O(1) avg O(n)

Two Pointers O(n) O(1)

BFS / DFS O(V+E) O(V)

You might also like