LeetCode 165. 比较版本号
题目描述


题意分析
版本号由点号分隔的若干修订号组成,比较时从左到右,逐段比较数值。前面的段都相等时,第一对数值不同的段决定结果:第一个版本更大返回
1,更小返回-1,全部相等返回0。每段的前导零不影响数值;如果一个版本提前结束,它缺少的后续段全部视为
0。因此字符串长度、修订号的位数以及段数都不能直接决定大小,也不能把整个版本号当作小数比较。题目保证输入是有效版本号,每段数值都能放入 32 位整数,所以可以逐段用整数解析,无需额外处理非法点号或超范围数字。
解法:双指针逐段解析
核心思路
[!blue]
比较顺序由修订号的位置决定,后面的段不能推翻前面已经出现的大小关系。因此不必先拆分并保存所有段,只需要用
i、j指向两个版本中下一段的开头,每轮完整读出对应的一段,比较后再决定是否继续。读取一段时,把段值从
0开始累积。每读入一个数字,就用num = num * 10 + digit把原值左移一个十进制位,再加入当前数字;读到点号或字符串结尾时,这一段结束。前导零在累积过程中不会改变数值,因此自然被忽略。题目保证最终段值不超过整数范围,这里的逐位累积也是安全的。每轮必须重新把两个段值置为
0。如果一方已经读完,它的解析循环不会执行,段值就保持为0,正好代表缺失段;另一方仍然继续读取。外层循环因此使用“至少一方未结束”的条件,直到两个版本都处理完。每轮开始时,之前比较过的所有对应段都相等。本轮若不同,就可以立即返回大小关系;本轮仍相等,就从已越过点号的位置继续下一轮。若循环正常结束,说明所有实际段及补出的零段都相等,返回
0。指针只向右移动,不需要切分数组或创建子串。
解题步骤
- 将两个指针
i、j初始化为0;只要任一指针尚未到达字符串末尾,就开始一轮比较。- 将
num1置为0,从version1[i]开始累积数字,直到点号或末尾;若停在点号上,再将i前移一次。- 同样读取
version2的当前段到num2。已结束的一方不移动,段值保持0。- 若
num1 > num2返回1,若num1 < num2返回-1;相等则继续读取后续段。- 两个版本都读完仍未发现不同,返回
0。
代码实现
class Solution {
public int compareVersion(String version1, String version2) {
int i = 0;
int j = 0;
while (i < version1.length() || j < version2.length()) {
// 每轮重新按零开始,缺失尾段自然作为零参与比较。
int num1 = 0;
while (i < version1.length() && version1.charAt(i) != '.') {
num1 = num1 * 10 + version1.charAt(i) - '0';
i++;
}
// 完整数字段结束后,有点号才越过它;耗尽的一侧后续按零段处理。
if (i < version1.length()) {
i++;
}
int num2 = 0;
while (j < version2.length() && version2.charAt(j) != '.') {
num2 = num2 * 10 + version2.charAt(j) - '0';
j++;
}
if (j < version2.length()) {
j++;
}
// 前面各段相等时,第一处不同就决定版本大小。
if (num1 > num2) {
return 1;
}
if (num1 < num2) {
return -1;
}
}
return 0;
}
}
func compareVersion(version1 string, version2 string) int {
i := 0
j := 0
for i < len(version1) || j < len(version2) {
// 每轮重新按零开始,缺失尾段自然作为零参与比较。
num1 := 0
for i < len(version1) && version1[i] != '.' {
num1 = num1*10 + int(version1[i]-'0')
i++
}
// 完整数字段结束后,有点号才越过它;耗尽的一侧后续按零段处理。
if i < len(version1) {
i++
}
num2 := 0
for j < len(version2) && version2[j] != '.' {
num2 = num2*10 + int(version2[j]-'0')
j++
}
if j < len(version2) {
j++
}
// 前面各段相等时,第一处不同就决定版本大小。
if num1 > num2 {
return 1
}
if num1 < num2 {
return -1
}
}
return 0
}
复杂度分析
- 时间复杂度:$O(m + n)$,
m、n分别是两个版本字符串的长度;每个字符最多被指针扫描一次,提前发现不同可以立即结束。- 空间复杂度:$O(1)$,只保存两个指针和当前两段的整数值,不创建分段数组。
关键点总结
[!green]
- 比较单位是对应位置修订号的数值,而不是整个字符串的字典序。
- 每轮重新置零,同时解决前导零与缺失尾段的处理。
- 前面各段相等是继续比较的前提,第一处不同就决定完整版本号的大小。
- “或”保证较长版本的尾段仍会被检查,末尾多出的全零段则不影响相等结论。
易错点总结
[!yellow]
- 直接按字符比较,会把数字位数和前导零误当成修订号的大小关系。
- 外层循环写成“且”,会在较短版本结束时提前停止,漏掉另一方可能非零的尾段。
- 不在每轮重置段值,会把上一段的数值累积进下一段。
- 读完数字后忘记越过点号,指针会停在原处,无法结束循环。
- 根据段数直接判断大小,会忽略缺失段按零处理的规则;必须继续比较剩余段的实际值。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 8. 字符串转换整数 (atoi) | 中等 | 同样解析十进制数字,本题每段分别比较且忽略前导零,不把完整版本号当一个整数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!