102道LeetCode题解全解析:Swift 5编程范式与算法优化实战指南
你是否还在为Swift算法面试题挠头?面对LeetCode上纷繁复杂的解题思路,是否渴望一套既符合Swift语言特性又保证最优时间/空间复杂度的权威指南?本文将带你深入剖析102道LeetCode经典题目,通过Swift 5的独特语法特性,从根本上提升你的算法思维与代码质量。
读完本文你将获得:
- 102道LeetCode题目(Easy/Medium/Hard)的Swift 5最优解
- 掌握Swift语言特性在算法中的实战应用(guard/defer、map/filter/reduce等)
- 算法复杂度分析方法论(从O(N²)到O(logN)的优化路径)
- 完整测试驱动开发(TDD)实践案例
- 10+种数据结构与算法范式的Swift实现模板
项目架构深度解析
LeetCode-Solutions-in-Swift项目采用模块化架构设计,严格遵循Swift最佳实践,构建了一套可扩展的算法解决方案体系。项目核心结构如下:
LeetCode-Solutions-in-Swift/
├── LICENSE
├── README.md
└── Solutions/
├── Solutions.xcodeproj/ # Xcode项目配置
├── Solutions/ # 核心代码目录
│ ├── Easy/ # 简单难度题目
│ ├── Medium/ # 中等难度题目
│ ├── Hard/ # 困难难度题目
│ └── Solutions_ObjC/ # Objective-C对比实现
└── SolutionsTests/ # 单元测试目录
├── Easy/
├── Medium/
└── Hard/
核心技术特点
该项目区别于其他算法仓库的四大优势:
-
Swift原生优化:充分利用Swift语言特性,如可选类型(Optional)、扩展(Extension)、泛型(Generics)等,实现更简洁优雅的代码
-
严格复杂度控制:所有解决方案均保证最优时间/空间复杂度,例如二叉树中序遍历的O(1)空间实现
-
双语言对照:部分题目提供Swift与Objective-C双实现,便于理解两种语言的范式差异
-
全面测试覆盖:每个算法配备多组测试用例,包括边界条件、性能压力测试等
Swift算法实现范式
哈希表应用:Two Sum问题的Swift实现
Two Sum作为LeetCode开篇第一题(Medium难度),其最优解法充分体现了哈希表在空间换时间策略中的经典应用。该项目采用Swift字典(Dictionary)实现O(N)时间复杂度的解决方案:
struct Medium_001_Two_Sum {
// t = O(N), s = O(N)
static func twoSum(numbers: [Int], target: Int) -> [Int] {
var hashMap = [Int: Int]() // 存储值到索引的映射
var result = [Int]()
for i in 0..<numbers.count {
let numberToFind: Int = target - numbers[i]
// 检查差值是否已存在于哈希表中
if let numberToFindIndex = hashMap[numberToFind] {
result.append(numberToFindIndex + 1) // 题目要求非零基索引
result.append(i + 1)
return result
} else {
hashMap[numbers[i]] = i // 存储当前值及其索引
}
}
return result
}
}
测试用例设计
单元测试采用XCTest框架,通过多场景覆盖确保算法正确性:
func test_001() {
let input0: [Int] = [2, 7, 11, 15]
let input1: Int = 9
let expected: [Int] = [1, 2]
asyncHelper(input0: input0, input1: input1, expected: expected)
}
func test_002() {
let input0: [Int] = [5, 5] // 处理重复元素
let input1: Int = 10
let expected: [Int] = [1, 2]
asyncHelper(input0: input0, input1: input1, expected: expected)
}
func test_003() {
let input0: [Int] = [5, 7, 5, 2, 11, 15] // 多个潜在解
let input1: Int = 9
let expected: [Int] = [2, 4] // 确保返回最前的有效解
asyncHelper(input0: input0, input1: input1, expected: expected)
}
Swift vs Objective-C实现对比
| 特性 | Swift实现 | Objective-C实现 |
|---|---|---|
| 类型安全 | 编译时严格类型检查 | 运行时动态类型 |
| 代码简洁度 | 30行(含注释) | 55行(含注释) |
| 内存管理 | ARC自动管理,无需显式释放 | 需要手动管理或使用ARC |
| 集合操作 | 原生Dictionary类型 | NSDictionary,需处理nil |
字符串操作:ZigZag Conversion的Swift优化
第6题ZigZag Conversion(Easy难度)展示了Swift字符串处理的独特优势。由于Swift字符串不支持下标随机访问(因采用扩展 grapheme 簇存储),项目实现了高效的字符数组转换策略:
struct Easy_006_ZigZag_Conversion {
// t = O(N), s = O(N)
static func convert(_ s: String, _ numRows: Int) -> String {
guard numRows > 1 else { return s }
let characters = Array(s)
var rows = [String](repeating: "", count: numRows)
var currentRow = 0
var goingDown = false
for char in characters {
rows[currentRow] += String(char)
// 到达边界时改变方向
if currentRow == 0 || currentRow == numRows - 1 {
goingDown.toggle()
}
currentRow += goingDown ? 1 : -1
}
return rows.joined()
}
}
算法可视化
ZigZag转换过程可通过以下状态图直观理解:
动态规划:最长回文子串的Manacher算法
第5题Longest Palindromic Substring(Medium难度)采用了Manacher算法实现O(N)时间复杂度,充分展示了Swift在复杂算法实现中的表达能力:
struct Medium_005_Longest_Palindromic_Substring {
static func longestPalindrome(_ s: String) -> String {
let str = preprocess(s)
let n = str.count
var p = [Int](repeating: 0, count: n)
var center = 0, right = 0
for i in 1..<n-1 {
let mirror = 2 * center - i
if i < right {
p[i] = min(right - i, p[mirror])
}
// 尝试扩展回文
var a = i + (1 + p[i])
var b = i - (1 + p[i])
while str[str.index(str.startIndex, offsetBy: a)] == str[str.index(str.startIndex, offsetBy: b)] {
p[i] += 1
a += 1
b -= 1
}
// 更新中心和右边界
if i + p[i] > right {
center = i
right = i + p[i]
}
}
// 找到最长回文
var maxLen = 0, centerIndex = 0
for i in 1..<n-1 {
if p[i] > maxLen {
maxLen = p[i]
centerIndex = i
}
}
let start = str.index(str.startIndex, offsetBy: (centerIndex - maxLen))
let end = str.index(str.startIndex, offsetBy: (centerIndex + maxLen))
let subStr = str[start..<end]
return String(subStr.filter { $0 != "#" })
}
private static func preprocess(_ s: String) -> String {
guard !s.isEmpty else { return "^$" }
var result = "^"
for char in s {
result += "#\(char)"
}
result += "#$"
return result
}
}
测试驱动开发实践
项目采用XCTest框架实现全面的测试覆盖,每个算法至少包含3组测试用例:基本功能测试、边界条件测试和性能压力测试。以Two Sum的测试实现为例:
class Medium_001_Two_Sum_Test: XCTestCase, SolutionsTestCase {
func test_001() {
let input0: [Int] = [2, 7, 11, 15]
let input1: Int = 9
let expected: [Int] = [1, 2]
asyncHelper(input0: input0, input1: input1, expected: expected)
}
func test_002() {
let input0: [Int] = [5, 5] // 重复元素测试
let input1: Int = 10
let expected: [Int] = [1, 2]
asyncHelper(input0: input0, input1: input1, expected: expected)
}
func test_003() {
let input0: [Int] = [5, 7, 5, 2, 11, 15] // 多个潜在解测试
let input1: Int = 9
let expected: [Int] = [2, 4]
asyncHelper(input0: input0, input1: input1, expected: expected)
}
// 异步测试辅助函数
private func asyncHelper(input0: [Int], input1: Int, expected: [Int]) {
weak var expectation: XCTestExpectation? = self.expectation(description: timeOutName())
serialQueue().async {
let result_swift = Medium_001_Two_Sum.twoSum(numbers: input0, target: input1)
let result_objc = ObjC_Medium_001_Two_Sum.twoSum(input0 as [NSNumber], target: input1)
// 验证Swift实现
assertHelper(expected == result_swift,
problemName: self.problemName(),
input: input0,
resultValue: result_swift,
expectedValue: expected)
// 验证Objective-C实现
let result_objc_Int = result_objc.map { $0.intValue }
assertHelper(result_objc_Int == expected,
problemName: self.problemName(),
input: input0,
resultValue: result_objc,
expectedValue: expected)
expectation?.fulfill()
}
waitForExpectations(timeout: timeOut()) { error in
if error != nil {
assertHelper(false,
problemName: self.problemName(),
input: input0,
resultValue: self.timeOutName(),
expectedValue: expected)
}
}
}
}
按难度分级的学习路径
入门必刷(Easy难度)
| 题目 | 算法类型 | 核心知识点 | 时间复杂度 |
|---|---|---|---|
| 7. Reverse Integer | 数学运算 | 整数溢出处理 | O(logN) |
| 9. Palindrome Number | 数学运算 | 回文判断 | O(logN) |
| 13. Roman to Integer | 哈希表 | 符号映射 | O(N) |
| 14. Longest Common Prefix | 字符串 | 垂直扫描 | O(N*M) |
| 20. Valid Parentheses | 栈 | 括号匹配 | O(N) |
| 21. Merge Two Sorted Lists | 链表 | 指针操作 | O(N+M) |
| 26. Remove Duplicates | 数组 | 双指针 | O(N) |
进阶提升(Medium难度)
| 题目 | 算法类型 | 核心知识点 | 时间复杂度 |
|---|---|---|---|
| 1. Two Sum | 哈希表 | 空间换时间 | O(N) |
| 2. Add Two Numbers | 链表 | 进位处理 | O(max(N,M)) |
| 3. Longest Substring | 滑动窗口 | 无重复字符 | O(N) |
| 5. Longest Palindrome | 动态规划 | 中心扩展 | O(N²) |
| 11. Container Water | 双指针 | 面积计算 | O(N) |
| 15. 3Sum | 双指针 | 排序去重 | O(N²) |
| 17. Letter Combinations | 回溯法 | 组合生成 | O(3^N) |
面试挑战(Hard难度)
| 题目 | 算法类型 | 核心知识点 | 时间复杂度 |
|---|---|---|---|
| 4. Median of Two Arrays | 二分查找 | 寻找中位数 | O(log(min(N,M))) |
| 10. Regular Expression | 动态规划 | 模式匹配 | O(N*M) |
| 23. Merge k Sorted Lists | 堆 | 多路归并 | O(NlogK) |
| 32. Longest Valid Parentheses | 动态规划 | 括号匹配 | O(N) |
| 42. Trapping Rain Water | 双指针 | 雨水计算 | O(N) |
| 76. Minimum Window Substring | 滑动窗口 | 子串覆盖 | O(N+M) |
| 84. Largest Rectangle | 栈 | 直方图计算 | O(N) |
项目使用指南
环境要求
- Xcode 12.4 (12D4e) 或更高版本
- Swift 5.3 或更高版本
- macOS 10.15 或更高版本
快速开始
-
克隆仓库:
git clone https://gitcode.com/gh_mirrors/le/LeetCode-Solutions-in-Swift.git -
打开项目:
cd LeetCode-Solutions-in-Swift open Solutions/Solutions.xcodeproj -
运行所有测试:
- 在Xcode中按下
⌘ + U - 或使用命令行:
xcodebuild test -project Solutions/Solutions.xcodeproj -scheme Solutions -destination 'platform=iOS Simulator,name=iPhone 12'
- 在Xcode中按下
测试结果分析
测试套件包含665个测试用例,覆盖所有102道题目。测试报告将显示:
- 每个算法的执行时间
- 内存使用情况
- Swift与Objective-C实现的性能对比
- 边界条件处理情况
性能优化策略
时间复杂度优化案例
以第4题Median of Two Sorted Arrays(Hard难度)为例,项目实现了O(log(min(N,M)))的最优解法,关键在于通过二分查找减少比较次数:
struct Hard_004_Median_Of_Two_Sorted_Arrays {
static func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double {
// 确保nums1是较短的数组,优化查找范围
let (numsA, numsB) = nums1.count <= nums2.count ? (nums1, nums2) : (nums2, nums1)
let m = numsA.count, n = numsB.count
var imin = 0, imax = m, halfLen = (m + n + 1) / 2
while imin <= imax {
let i = (imin + imax) / 2
let j = halfLen - i
// 调整二分查找范围
if i < m && numsB[j-1] > numsA[i] {
imin = i + 1
} else if i > 0 && numsA[i-1] > numsB[j] {
imax = i - 1
} else {
// 找到正确划分
let maxLeft: Int
if i == 0 {
maxLeft = numsB[j-1]
} else if j == 0 {
maxLeft = numsA[i-1]
} else {
maxLeft = max(numsA[i-1], numsB[j-1])
}
// 奇数长度直接返回
if (m + n) % 2 == 1 {
return Double(maxLeft)
}
// 偶数长度计算平均值
let minRight: Int
if i == m {
minRight = numsB[j]
} else if j == n {
minRight = numsA[i]
} else {
minRight = min(numsA[i], numsB[j])
}
return Double(maxLeft + minRight) / 2.0
}
}
return 0.0
}
}
空间复杂度优化:O(1)空间的二叉树遍历
第94题Binary Tree Inorder Traversal(Medium难度)展示了如何在不使用递归栈的情况下实现O(1)空间复杂度的中序遍历:
struct Medium_094_Binary_Tree_Inorder_Traversal {
// Morris Traversal: t = O(N), s = O(1)
static func inorderTraversal(_ root: TreeNode?) -> [Int] {
var result = [Int]()
var current = root
var predecessor: TreeNode?
while current != nil {
if current?.left == nil {
// 当前节点左子树为空,输出当前节点
result.append(current!.val)
current = current?.right
} else {
// 找到当前节点的前驱节点
predecessor = current?.left
while predecessor?.right != nil && predecessor?.right !== current {
predecessor = predecessor?.right
}
if predecessor?.right == nil {
// 建立线索
predecessor?.right = current
current = current?.left
} else {
// 恢复树结构,输出当前节点
predecessor?.right = nil
result.append(current!.val)
current = current?.right
}
}
}
return result
}
}
总结与展望
LeetCode-Solutions-in-Swift项目不仅提供了102道题目的最优解,更重要的是展示了如何将Swift语言特性与算法思想完美结合。通过学习这些实现,你将获得:
- Swift编程范式:掌握guard/defer、map/filter/reduce等高阶特性在算法中的应用
- 算法思维提升:从暴力解法到最优解的思考过程,培养时间/空间复杂度意识
- 测试驱动开发:学习如何设计全面的测试用例,确保代码正确性与鲁棒性
未来计划
- 增加更多题目,目标覆盖LeetCode前300题
- 添加算法复杂度可视化工具
- 实现SwiftUI交互式算法演示
- 提供iPadOS/iOS应用版本,支持离线学习
收藏本文,开启你的Swift算法精进之旅!如有任何问题或建议,欢迎在项目中提交issue,让我们共同打造Swift算法学习的优质资源库。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



