版本号升级统计
问题背景
在软件开发中,产品通过版本号来区分不同的迭代。我们需要实现一个逻辑,来比较一个新版本和一组已发布的旧版本,并统计出其中有多少个版本是低于新版本的(即需要升级)。
版本号格式
- 版本号由一个或多个数字段组成。
- 这些数字段之间由点(
.
)连接。 - 例如:
1
,1.0
,1.2.03.04.56
都是合法的版本号。 - 版本
1.0
,1.0.0.0
,1.00.00
被视为不同的版本字符串,但它们的数值比较遵循下述规则。
版本比较规则
要比较两个版本号(例如 v1
和 v2
),我们遵循以下步骤:
-
分段 : 将两个版本号按点(
.
)分割成数字段列表。例如,"100.05.0"
变为[100, 5, 0]
。 -
逐段比较: 从左到右(从第一个段开始)依次比较两个列表对应位置的数字。
- 如果
v1
的某段数字 小于v2
的对应段数字,则v1
低于v2
,比较结束。 - 如果
v1
的某段数字 大于v2
的对应段数字,则v1
高于v2
,比较结束。 - 如果两段数字相等,则继续比较下一段。
- 如果
-
长度不同处理 : 如果在比较完所有共同长度的段后,数字都完全相同(例如比较
100.50
和100.50.1
),那么段数更少的版本为更低版本。
任务要求
给定一个已发布版本的列表 oldVersions
和一个新版本 newVersion
,请统计出 oldVersions
中有多少个版本是低于 newVersion
的,并返回这个数量。
输入格式
-
oldVersions
: 第一个参数,一个字符串列表,代表已发布的版本号。0 <= oldVersions.length <= 1000
(样例显示为10,但可能更大)1 <= oldVersions[i].length <= 50
- 已发布版本列表中无重复版本号。
-
newVersion
: 第二个参数,一个字符串,代表新版本号。1 <= newVersion.length <= 50
-
版本号段值 :
0 <= 每个数字段的值 < 2^31
。
输出格式
- 一个整数,表示
oldVersions
中需要升级的版本数量。
样例说明
样例 1
-
输入:
oldVersions = ["100.200", "20.500", "100.5", "100.05.0", "0.0.0.0", "100.50.1", "100.50.0"]
newVersion = "100.50"
-
输出 :
4
-
解释:
我们将每个 oldVersions 中的版本与新版本 100.50 进行比较:
已发布版本 | 比较过程 | 结果 | 需要升级? |
---|---|---|---|
"100.200" |
第1段: 100=100。第2段: 200 > 50。 | 更高 | 否 |
"20.500" |
第1段: 20 < 100。 | 更低 | 是 |
"100.5" |
第1段: 100=100。第2段: 5 < 50。 | 更低 | 是 |
"100.05.0" |
第1段: 100=100。第2段: 5 < 50。 | 更低 | 是 |
"0.0.0.0" |
第1段: 0 < 100。 | 更低 | 是 |
"100.50.1" |
前2段相同,但它有第3段,而新版本没有。 | 更高 | 否 |
"100.50.0" |
前2段相同,但它有第3段,而新版本没有。 | 更高 | 否 |
markdown
总计有 **4** 个版本需要升级。
样例 2
-
输入:
oldVersions = []
newVersion = "5.0.0"
-
输出 :
0
-
解释:
已发布版本列表为空,因此需要升级的版本数量为 0。
java
import java.util.Arrays;
import java.util.Comparator; // 仅用于另一种排序写法,本解法直接比较
import java.util.Scanner;
public class Solution {
/**
* 核心比较函数:比较两个版本号字符串 version1 和 version2 的大小。
*
* @param version1 第一个版本号字符串
* @param version2 第二个版本号字符串
* @return -1 如果 version1 < version2
* 0 如果 version1 == version2
* 1 如果 version1 > version2
*/
private int compareVersions(String version1, String version2) {
// 1. 使用 "\." 作为分隔符分割版本号。
// '.' 在正则表达式中是特殊字符,需要用两个反斜杠转义。
String[] parts1 = version1.split("\\.");
String[] parts2 = version2.split("\\.");
// 2. 确定需要比较的总轮次,即两个版本号段数的最大值。
int maxLength = Math.max(parts1.length, parts2.length);
// 3. 从左到右(从最高位段到最低位段)逐段比较。
for (int i = 0; i < maxLength; i++) {
// a. 获取当前段的数值。
// 如果某个版本号已经没有后续段了(即 i 超出了其 parts 数组的长度),
// 则其当前段的数值视为 0。
// 例如,比较 "1.2" 和 "1.2.1" 时,在第三轮 (i=2):
// "1.2" 的第三段值视为 0,而 "1.2.1" 的第三段值是 1。
// 这正确地实现了"短的版本更小"的规则。
int val1 = (i < parts1.length) ? Integer.parseInt(parts1[i]) : 0;
int val2 = (i < parts2.length) ? Integer.parseInt(parts2[i]) : 0;
// b. 比较当前段的数值
if (val1 < val2) {
return -1; // 发现 version1 更低,立即返回
}
if (val1 > val2) {
return 1; // 发现 version1 更高,立即返回
}
// 如果当前段相等,则继续比较下一段
}
// 4. 如果所有已比较的段都相等,则两个版本号相等。
// 例如 "1.0" 和 "1.0.0.0",在比较前两段时都相等,
// 后续 "1.0" 的段视为 0,"1.0.0.0" 的段也是 0,所以最终相等。
// 注意:题目说 "1.0", "1.0.0.0" 视为不同版本,这是指它们在输入列表中是
// 不同的字符串个体,但在数值比较上是等价的。
return 0;
}
/**
* 主逻辑方法:统计需要升级的版本数量。
*
* @param oldVersions 已发布的版本号列表 (字符串数组)
* @param newVersion 新发布的版本号 (字符串)
* @return 需要升级的版本数量
*/
public int countUpgrades(String[] oldVersions, String newVersion) {
// 处理边界情况:如果已发布版本列表为空或 null,则没有需要升级的版本。
if (oldVersions == null || oldVersions.length == 0) {
return 0;
}
int upgradeCount = 0; // 初始化需要升级的版本计数器
// 遍历每一个旧版本
for (String oldVersion : oldVersions) {
// 调用比较函数,判断旧版本是否低于新版本
// compareVersions 返回 -1 表示 oldVersion < newVersion
if (compareVersions(oldVersion, newVersion) < 0) {
upgradeCount++; // 如果是,则升级计数加 1
}
}
return upgradeCount; // 返回总数
}
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取第一行:已发布版本列表的字符串表示
String oldVersionsLine = scanner.nextLine();
// 读取第二行:新版本的字符串
String newVersionLine = scanner.nextLine();
scanner.close();
// --- 解析输入 ---
// 解析形如 ["100.200", "20.500", ...] 的字符串
String[] oldVersions = parseStringArray(oldVersionsLine);
// 新版本字符串可能也带有引号,进行清理
String newVersion = newVersionLine.replaceAll(""", "").trim();
// 创建 Solution 类的实例并调用方法
Solution solution = new Solution();
int result = solution.countUpgrades(oldVersions, newVersion);
// 输出结果
System.out.println(result);
}
/**
* 辅助方法:解析形如 ["a", "b", "c"] 的输入字符串。
* @param line 输入行
* @return 字符串数组
*/
private static String[] parseStringArray(String line) {
if (line == null || line.length() <= 2) { // 至少应包含 "[]"
return new String[0];
}
// 移除首尾的 '[' 和 ']'
line = line.trim();
if (line.startsWith("[") && line.endsWith("]")) {
line = line.substring(1, line.length() - 1);
}
if (line.isEmpty()) {
return new String[0];
}
// 按 "," 分割,并去除每个元素首尾的引号 " 和空格
return Arrays.stream(line.split(","))
.map(s -> s.trim().replaceAll(""", ""))
.toArray(String[]::new);
}
}