版本号比较 | 豆包MarsCode AI刷题

162 阅读6分钟

版本号比较

题目要求

在某个项目中,每个版本都用版本号标记,由一个或多个修订号组成,修订号之间由点号.分隔。每个修订号可能有多位数字,并且可能会包含前导零。你需要根据两个版本号 version1 和 version2,判断哪个版本更新,或者它们是否相同。 例如,2.5.33 和 0.1 都是有效的版本号。 当比较两个版本时,从左到右依次比较它们的修订号。忽略每个修订号的前导零,直接比较修订号对应的整数值。如果其中一个版本没有足够的修订号,缺失部分默认补为0

  • 如果 version1 > version2,返回 1
  • 如果 version1 < version2,返回 -1
  • 如果两个版本相等,返回 0

测试样例

样例1:

输入:version1 = "0.1" , version2 = "1.1"
输出:-1

样例2:

输入:version1 = "1.0.1" , version2 = "1"
输出:1

样例3:

输入:version1 = "7.5.2.4" , version2 = "7.5.3"
输出:-1

样例4:

输入:version1 = "1.0" , version2 = "1.0.0"
输出:0

算法思路

  1. 去除点号

    • 创建了两个空字符串s1s2
    • 通过遍历version1version2,将所有非点号(.)的字符添加到s1s2中。这一步的目的是将版本号中的点号去掉,以便将剩余的数字字符串转换为整数进行比较。
  2. 长度调整

    • 检查s1s2的长度,如果它们不相等,您通过在较短的字符串后面添加零('0')来使它们长度相等。这一步是为了处理版本号长度不一致的情况,确保两个版本号在比较时有相同数量的修订号。
  3. 转换为整数

    • 使用std::stois1s2转换为整数num1num2。这一步是将字符串形式的版本号转换为数值形式,以便进行数值比较。
  4. 比较整数

    • 比较num1num2的值,如果num1大于num2,则返回1;如果num1小于num2,则返回-1;如果两者相等,则返回0。

时间复杂度分析

  1. 去除点号:两个for循环分别遍历version1version2字符串,将非点号字符添加到s1s2字符串中。每个循环的时间复杂度是O(n),其中n是对应版本号字符串的长度。因此,这部分的时间复杂度是O(n + m),其中n是version1的长度,m是version2的长度。
  2. 补零:如果s1s2的长度不同,代码会通过添加零来使它们长度相等。这部分的时间复杂度是O(k),其中k是s1s2长度之差的绝对值。在最坏的情况下,k可以等于版本号的最大长度,因此这部分的时间复杂度可以认为是O(n),其中n是两个版本号中较长的那个的长度。
  3. 转换为整数并比较:使用std::stoi函数将字符串转换为整数并进行比较。std::stoi函数的时间复杂度是O(p),其中p是字符串的长度。由于我们已经确保了s1s2的长度相等,这部分的时间复杂度是O(p),其中p是s1s2的长度。

综合以上各部分,整体时间复杂度是O(n + m + p),其中n是version1的长度,m是version2的长度,p是处理后s1s2的最大长度。在最坏的情况下,如果两个版本号都非常长,时间复杂度将接近O(n + m + p)。

代码

#include "iostream"
#include <string>
int solution(std::string version1, std::string version2) {
  // Edit your code here
  std::string s1, s2;
  for (int i = 0; i < version1.length(); i++) {
    if (version1[i] != '.')
      s1 += version1[i];
  }
  for (int i = 0; i < version2.length(); i++) {
    if (version2[i] != '.')
      s2 += version2[i];
  }
  //std::cout << s1 << " " << s2 << std::endl;
  int a;
  if (s1.length() > s2.length()) {
    a = s1.length() - s2.length();
    for (int i = 0; i < a; i++)
      s2 += '0';
  }
  if (s1.length() < s2.length()) {
    a = s2.length() - s1.length();
    for (int i = 0; i < a; i++)
      s1 += '0';
  }

  int num1 = std::stoi(s1);
  int num2 = std::stoi(s2);
  //std::cout << num1 << " " << num2 << std::endl;
  if (num1 > num2)
    return 1;
  else if (num1 < num2)
    return -1;
  else
    return 0;

  return -1;
}

优化

  1. 代码问题:将整个版本号转换为一个整数可能会非常低效,特别是当版本号很长时。此外,这种方法不能很好地处理不同长度的版本号,因为它假设所有修订号都是同等重要的,而实际上,版本号的比较是从左到右进行的,每个修订号都可能有不同的权重。
  2. 优化:在拆分版本号时转换为整数,避免了不必要的字符串拼接和后续的字符串到整数的转换。逐个比较版本号的每个部分,而不是将整个版本号转换为一个整数。这样可以避免处理非常大的整数,并且在找到第一个不同的修订号时立即返回结果,而不是等待整个字符串转换完成。

优化后的代码

#include <iostream>
#include <sstream>
#include <vector>
#include <string>

int solution(std::string version1, std::string version2) {
    // 拆分版本号
    std::istringstream iss1(version1);
    std::istringstream iss2(version2);
    std::vector<int> nums1, nums2;
    std::string token;
    while (std::getline(iss1, token, '.')) {
        nums1.push_back(std::stoi(token));
    }
    while (std::getline(iss2, token, '.')) {
        nums2.push_back(std::stoi(token));
    }

    // 逐个比较修订号
    int len = std::max(nums1.size(), nums2.size());
    for (int i = 0; i < len; ++i) {
        int num1 = i < nums1.size() ? nums1[i] : 0;
        int num2 = i < nums2.size() ? nums2[i] : 0;
        if (num1 > num2) {
            return 1;
        } else if (num1 < num2) {
            return -1;
        }
    }

    // 如果所有修订号都相等,返回0
    return 0;
}

优化后的时间复杂度

  1. 拆分版本号:使用std::getlinestd::stoi来拆分版本号字符串为整数数组。这个过程的时间复杂度是O(n),其中n是版本号中字符的数量。这是因为我们需要遍历整个字符串来拆分修订号,并且对于每个修订号,我们需要将其转换为整数。
  2. 逐个比较修订号:我们比较两个版本号的修订号,这个过程的时间复杂度是O(m),其中m是两个版本号中较长的修订号数组的长度。这是因为我们需要遍历较长的数组来比较每个修订号。

因此,总的时间复杂度是O(n + m),其中n是version1中字符的数量,m是version2中字符的数量。在最坏的情况下,如果两个版本号都非常长,时间复杂度将接近O(n + m)。