LeetCode #165 Medium

Compare Version Numbers

Compare dotted version strings numerically per chunk: return −1, 0, or 1. "1.01" == "1.001", "1.0" == "1".

Constraints
  • 1 <= version1.length, version2.length <= 500
  • version1 and version2 only contain digits and '.'.
  • version1 and version2 are valid version numbers.
  • All the given revisions in version1 and version2 can be stored in a 32-bit integer.
stringtwo-pointers
Open on LeetCode ↗
02

Intuition

The task is to compare version numbers, which looks like a string problem and is really a numeric one. The instinct to compare two version strings lexicographically fails immediately: "1.10" sorts before "1.9" as text, because the character '1' precedes '9'. Numerically, revision 10 is clearly the later release. The dots are separators, not decimal points, so 1.10 is version one, revision ten — not "one point one zero". That single observation dictates the approach. Split both strings on dots, then compare the chunks as integers, position by position. Converting with int() also disposes of a second trap for free: "1.01" and "1.001" are the same version, because leading zeros carry no numeric meaning and int("01") == int("001") == 1. The last wrinkle is unequal lengths. Comparing "1.0" against "1" seems to run out of chunks on one side, but the intended reading is that an absent revision is zero: - A missing chunk counts as 0, which is why "1.0", "1.0.0" and "1" are all the same version. So iterate over the longer of the two chunk lists and substitute 0 whenever one side has run out. The first position where the two differ decides the whole comparison; if you get through every position without a difference, the versions are equal.

How to spot this pattern

The trap in compare version numbers is treating a version as a number or a string; it's neither — it's a sequence of integers compared left to right. Once you see it that way, the two remaining questions answer themselves: parse each part as an int (killing leading zeros), and treat missing trailing parts as 0 so 1.0 and 1 compare equal.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what you are recomputing on every character that could be carried instead. Aim for O(n + m) time and O(n + m) space.

1

Split both strings on the dot

Break each version into its revision chunks. "1.10.3" becomes ["1", "10", "3"]. From here the original strings are irrelevant — all comparison happens between chunks, never between whole strings or individual characters.

2

Iterate to the length of the longer list

Loop max(len(a), len(b)) times rather than stopping at the shorter one. This is what lets "1.0.0" be recognised as equal to "1" instead of being cut short after the first chunk.

3

Treat a missing chunk as zero

At each position, take the chunk if it exists and use 0 otherwise. This is the rule that makes trailing .0 segments invisible to the comparison, exactly as the problem specifies.

4

Convert each chunk with int()

Comparing int(chunk) values solves two problems in one step: 10 > 9 comes out right where the string comparison got it backwards, and leading zeros vanish so "01" equals "1". This conversion is the heart of the solution — everything else is bookkeeping around it.

5

Return on the first difference

If the two integers differ, you already know the answer: −1 when the first is smaller, 1 when it is larger. Later chunks cannot overturn an earlier difference, since revisions are ordered most-significant first. Return immediately.

6

Equal all the way through means equal versions

If the loop completes without finding a difference, every position matched — including the implicit zeros — so return 0. This is the path taken by pairs like "1.0" and "1".

7

Cost of the comparison

Splitting is linear in the input length and the loop runs once per chunk, so time is O(n + m) for version strings of length n and m. Space is O(n + m) for the split lists, or O(1) if you walk both strings with two pointers and parse chunks in place — worth mentioning if the interviewer asks for constant space.

04

Solution & live demo

▶1class Solution:
▶2 def compareVersion(self, version1, version2):
▶3 a = [int(x) for x in version1.split(".")]
▶4 b = [int(x) for x in version2.split(".")]
▶5 for i in range(max(len(a), len(b))):
▶6 x = a[i] if i < len(a) else 0
▶7 y = b[i] if i < len(b) else 0
▶8 if x != y:
▶9 return 1 if x > y else -1
▶10 return 0
05

Common pitfalls

Comparing the strings directly

✗ Wrong
return (version1 > version2) - (version1 < version2)
✓ Right
a = [int(x) for x in version1.split(".")]

Lexicographic order puts "1.10" before "1.9" because '1' < '9', and "01" differs from "1". Parsing to integers is what makes 10 rank above 9 and leading zeros vanish.

Parsing as a float

✗ Wrong
if float(version1) > float(version2):
✓ Right
for i in range(max(len(a), len(b))):

A version can have more than one dot — float("1.2.3") raises, and even with one dot 1.10 becomes 1.1, ranking it below 1.9. Each component is independent, not a decimal fraction.

Stopping at the shorter version

✗ Wrong
for i in range(min(len(a), len(b))):
✓ Right
for i in range(max(len(a), len(b))):
    x = a[i] if i < len(a) else 0
    y = b[i] if i < len(b) else 0

1.0.1 versus 1 must report greater, but stopping at the shorter one compares only the leading 1 and calls them equal. Absent components are implicitly zero, which also makes 1.0 equal 1.

06

Edge cases

Leading zeros, "1.01" vs "1.001"

int() normalizes both to 1 → equal.

Trailing zero chunks, "1.0.0" vs "1"

Missing chunks read as 0 → equal.

07

Complexity

Time
O(n + m)
Space
O(n + m)
Split + one comparison pass.