Swift在LeetCode上:如何开始并坚持不放弃
每个iOS开发者迟早都会遇到瓶颈:项目能做,应用能上架AppStore,但面试时遇到二叉树问题就卡壳。熟悉吗?LeetCode就是那个训练场,能把“我只是写UI”变成“我用算法思考”。但有个问题:网上大多数解法是用Python或Java写的,而Swift是一门有自己哲学的语言。在本文中,我们将探讨如何用Swift高效地解决LeetCode问题,该使用哪些数据结构,以及需要注意什么才能不浪费时间。
本文适合那些已经掌握Swift基础,但想提升算法思维的人。我们不会教语法——我们将学习如何思考。
为什么Swift是LeetCode的绝佳选择
很多人认为Swift只是用于UIKit和SwiftUI,但实际上,该语言为算法问题提供了强大的功能:
- 可选类型 — 安全处理nil,避免意外崩溃。
- 值类型(结构体) — 可预测的复制,没有引用带来的意外。
- 惰性计算和函数式方法 — map、filter、reduce能大大简化代码。
- 速度 — Swift编译为原生代码,通常比Python快2-3倍。
然而,也有一些陷阱:例如,Swift中的数组是结构体,复制大型数组可能代价高昂。但我们会单独讨论这一点。
数据结构:在LeetCode上使用Swift需要了解的内容
在开始解决问题之前,请确保你能熟练使用基本数据结构。以下是Swift的必知清单:
数组和切片(Array, ArraySlice)
Swift中的数组不仅仅是列表,而是具有O(1)索引访问的强大结构。但要注意:在开头插入/删除是O(n)。使用ArraySlice可以在处理子数组时避免复制数据。
let numbers = [1, 2, 3, 4, 5]let slice = numbers[1..<4] // ArraySlice,无复制print(slice) // [2, 3, 4]字典和集合(Dictionary, Set)
对于频率统计、查找重复项或双指针算法,它们是你最好的朋友。记住:Dictionary是哈希表,平均O(1),但在哈希函数不佳时可能退化到O(n)。
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, Queue)
Swift没有内置的栈和队列结构,但很容易用数组模拟。对于栈,使用append和popLast。对于队列,使用ArraySlice或双向链表,以避免出队时的O(n)操作。
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 List)
LeetCode喜欢考单向链表问题。Swift没有内置实现,但你可以定义一个类:
class ListNode { var val: Int var next: ListNode? init(_ val: Int) { self.val = val; self.next = nil }}这是链表反转、检测环或删除节点等问题的经典做法。
Swift问题的算法模式
为了每次不重复造轮子,记住几个覆盖LeetCode 80%问题的模板。
双指针(Two Pointers)
例