1. 项目概述
LeetCode 165题是一道经典的字符串比较问题,题目要求比较两个版本号version1和version2的大小。这道题看似简单,但实际处理起来需要考虑多种边界情况,是考察字符串处理和逻辑思维能力的典型题目。
作为刷过300+道LeetCode的老鸟,我最初做这道题时也踩了不少坑。今天我就从实际解题角度,详细拆解这道题的解题思路、易错点和优化技巧。无论你是刚开始刷题的新手,还是想巩固基础的中级选手,这篇解析都能帮你彻底掌握这类字符串比较问题的解法套路。
2. 题目分析与核心思路
2.1 题目要求解析
题目给出两个版本号字符串version1和version2,要求比较它们的大小。版本号是由点号分隔的数字字符串,例如"1.01"和"1.001"。比较规则如下:
- 忽略前导零(即1.01和1.001表示相同版本)
- 从左到右依次比较每个修订号
- 如果某个修订号缺失(如1.0和1.0.0),则认为缺失的修订号为0
最终返回:
- 1 如果version1 > version2
- -1 如果version1 < version2
- 0 如果version1 == version2
2.2 解题思路拆解
解决这个问题的核心在于正确处理字符串分割和数字比较。我的解题思路分为三个关键步骤:
- 字符串分割:使用点号作为分隔符将版本号拆分为修订号数组
- 修订号比较:逐个比较对应位置的修订号数值
- 处理不等长情况:当版本号长度不一致时,将较短的版本号剩余部分视为0
这里最容易出错的是前导零的处理和不等长情况的处理。很多同学第一次做时容易忽略这些边界条件。
3. 详细实现与代码解析
3.1 基础实现方案
下面是用Python实现的完整代码,我会逐段解析关键点:
def compareVersion(version1: str, version2: str) -> int: v1 = list(map(int, version1.split('.'))) v2 = list(map(int, version2.split('.'))) max_len = max(len(v1), len(v2)) for i in range(max_len): num1 = v1[i] if i < len(v1) else 0 num2 = v2[i] if i < len(v2) else 0 if num1 > num2: return 1 elif num1 < num2: return -1 return 0关键点解析:
split('.')将字符串按点号分割map(int, ...)将每个分段转换为整数,自动去除前导零max_len确定需要比较的最大分段数- 循环中处理不等长情况,超出长度的分段视为0
3.2 时间复杂度分析
该算法的时间复杂度是O(n+m),其中n和m分别是两个版本号的长度。空间复杂度也是O(n+m),因为需要存储分割后的数组。
实际上可以优化为O(1)空间复杂度,采用双指针法逐个字符处理,但代码会复杂很多。对于面试场景,基础实现已经足够。
4. 边界情况与测试用例
4.1 必须考虑的边界情况
这道题看似简单,但隐藏着多个容易出错的边界条件:
- 前导零:"1.01"和"1.001"应该相等
- 不等长版本号:"1.0"和"1.0.0"应该相等
- 大数字:"1.99999999999999999999"和"2.0"应该比较正确
- 单个版本号:"1"和"0"的比较
- 空字符串:虽然题目保证输入有效,但实际工程中需要考虑
4.2 测试用例设计
好的测试用例应该覆盖所有边界情况。我建议至少包含以下测试用例:
test_cases = [ ("1.01", "1.001", 0), # 前导零 ("1.0", "1.0.0", 0), # 不等长 ("0.1", "1.1", -1), # 正常比较 ("1.0.1", "1", 1), # 长vs短 ("7.5.2.4", "7.5.3", -1),# 中间差异 ("1.01", "1.10", -1), # 小数点后比较 ("1", "1.0.0.0.0", 0), # 多个零 ]5. 常见错误与优化技巧
5.1 新手常见错误
根据我在LeetCode讨论区和面试中的观察,这道题最常见的错误包括:
- 直接字符串比较:错误地认为"1.2" < "1.10"(实际上"1.10"更大)
- 忽略前导零:没有将"01"转换为数字1
- 不等长处理不当:没有将缺失的分段视为0
- 类型转换错误:忘记将字符串分段转换为整数
5.2 优化技巧与进阶思路
对于想进一步优化的同学,可以考虑以下改进:
- 双指针法:不分割字符串,直接逐个字符比较,空间复杂度降为O(1)
- 生成器表达式:使用生成器惰性计算,避免存储整个数组
- 提前终止:发现差异立即返回,不必比较剩余部分
这里给出双指针法的Python实现:
def compareVersion(version1: str, version2: str) -> int: n1, n2 = len(version1), len(version2) i = j = 0 while i < n1 or j < n2: num1 = 0 while i < n1 and version1[i] != '.': num1 = num1 * 10 + int(version1[i]) i += 1 num2 = 0 while j < n2 and version2[j] != '.': num2 = num2 * 10 + int(version2[j]) j += 1 if num1 > num2: return 1 elif num1 < num2: return -1 i += 1 j += 1 return 0这种实现虽然代码量稍多,但空间效率更高,适合对内存要求严格的场景。
6. 实际工程中的应用
版本号比较在实际工程中有广泛应用场景:
- 软件版本检查:判断当前版本是否需要升级
- 依赖管理:确定库版本是否满足要求
- API版本控制:路由不同版本的API请求
- 配置文件版本:处理配置文件的兼容性
在工程实现中,还需要考虑更多因素:
- 版本号可能包含字母(如"1.0a")
- 可能有特殊版本号(如"latest"、"snapshot")
- 需要处理更复杂的比较规则(如语义化版本)
7. 类似题目推荐
掌握这道题后,可以尝试以下类似题目巩固字符串处理技能:
- LeetCode 468 - 验证IP地址:更复杂的字符串分割和验证
- LeetCode 929 - 独特的电子邮件地址:字符串处理与规范化
- LeetCode 14 - 最长公共前缀:字符串逐个字符比较
- LeetCode 443 - 字符串压缩:字符串的遍历与统计
这些题目都考察了字符串处理的常见模式,建议按顺序练习,逐步提升字符串处理能力。