Algorithm

Compare Version Numbers

Arrays & Strings Pattern

Compare Version Numbers

Given two version strings v1 and v2, compare them. If v1 > v2 return 1; if v1 < v2 return -1; otherwise return 0. Treat missing segments as 0.

CONSTRAINTS
  • 1 <= v1.length, v2.length <= 500
  • v1/v2 consist of digits and '.'
EXAMPLE 1
Input: v1 = "1.01", v2 = "1.001"
Output: 0
Both versions have levels 1 and 1 — the leading zeros in '01' and '001' are cosmetic, so the levels are numerically equal.
EXAMPLE 2
Input: v1 = "1.0", v2 = "1.0.0"
Output: 0
The first two levels match; v1 has no third level, which counts as 0 and matches v2's 0. Equal.
EXAMPLE 3
Input: v1 = "1.10", v2 = "1.2"
Output: 1
The first level ties at 1. The second level is 10 versus 2, and 10 is the larger number, so v1 is greater.
Can I compare the version levels as strings instead of numbers?
No. '10' is greater than '2' numerically but smaller lexicographically, and '01' equals '1' numerically but differs as text. Each level must be parsed to an integer before comparing.
What happens when the two versions have a different number of levels?
Treat every missing level as 0 and compare up to the longer length. So '1.0' equals '1', but '1.0.1' is greater than '1'.
Can a level be larger than a 32-bit integer?
Possibly — a version string can be up to 500 characters. In languages with fixed-width ints, parse into a 64-bit / big integer, or compare digit strings carefully, to avoid overflow.
Are the inputs always well-formed (no empty levels or trailing dots)?
Assume so unless told otherwise — levels are non-negative integers separated by single dots. Worth confirming with the interviewer if the statement is silent on it.

A version string like "1.10.2" is a list of numbers separated by dots. Comparing two of them means walking the numbers left to right — the first level that differs decides the whole comparison — and returning 1 if v1 is larger, -1 if smaller, 0 if they are equal. Two rules make this trickier than it first looks, and both come straight from what a version number means: a leading zero inside a level is cosmetic ("01" is just the number 1), and a version that runs out of levels behaves as if the missing levels were 0 ("1.0" and "1" are the same version).

Why you cannot just compare the strings

The tempting shortcut is to compare v1 and v2 as plain strings, or to compare each dot-separated level as a string. It breaks immediately. Take "1.10" versus "1.2". Character by character the strings agree on "1." and then compare '1' against '2', so string comparison declares "1.10" the smaller. But version 1.10 comes after 1.2: numerically the second level is 10 versus 2, and 10 is larger. String ordering compares digit characters left to right, which has nothing to do with numeric size once two numbers have different digit counts. Leading zeros wreck it from the other direction: "1.01" and "1.1" are the same version, yet as strings "01" and "1" differ. The conclusion is unambiguous — each level must be compared as an integer, not as text.

Compare level by level, as numbers

So the plan writes itself. Split each version on the dots into its list of levels, turn each level into an integer (which erases leading zeros for free), and compare the two lists position by position. At the first position where the integers differ, we have the answer. If one list is shorter, its missing levels count as 0 — so we compare up to the longer length, reading a 0 whenever a list has run out.

python
def compare(version1, version2):
    a = version1.split(".")
    b = version2.split(".")
    for i in range(max(len(a), len(b))):
        x = int(a[i]) if i < len(a) else 0   # missing level -> 0
        y = int(b[i]) if i < len(b) else 0
        if x != y:
            return 1 if x > y else -1
    return 0
Crucial Noteiterate to max(len(a), len(b)), not the shorter length, reading a 0 for any level that has run out. Stopping at the shorter length would call "1.0.1" and "1" equal — it would never look at the trailing 1 that makes the first one larger. Padding the short version with zeros is exactly what makes "trailing zeros don't matter, but a trailing non-zero does" come out right.

If you would rather not allocate the split lists, the same logic runs with two pointers that build each level's integer on the fly and reset at every dot — trading the O(N) list memory for O(1) extra space, at the cost of a little more code. The comparison rule is identical.

Worked Example:v1 = "1.10", v2 = "1.2"
Split into levels: v1 → [1, 10], v2 → [1, 2].
- Level 0: 1 vs 1 — equal, keep going.
- Level 1: 10 vs 2 — differ. 10 > 2, so return 1. (Plain string comparison would have returned -1 here — the exact trap this problem is built around.)

Now an unequal-length case, v1 = "1.0", v2 = "1.0.0":
- Levels 0 and 1 tie (1 = 1, 0 = 0). At level 2, v1 has run out so we read 0, and v2 has 0 — equal. No difference anywhere, so return 0.

The cost, and the takeaway

Every character of both strings is read a constant number of times — once to parse, once to skip a dot — so the whole comparison is O(N + M) in the two lengths, with O(1) extra space in the pointer form. The transferable idea is smaller than the algorithms on other pages but appears constantly: when data arrives as text yet means something else — a number, a date, an IP address — compare it in the domain it means, not as raw characters. Here that means parse each level to an integer and pad the shorter list with zeros; the two bugs everyone hits (leading zeros, and mismatched level counts) both vanish the moment you do.

Interactive Strategy Visualization

version delta analyzer

Segment-wise Numerical Comparison
VERSION 1
1
0
1
VERSION 2
1
0
0
🔍

Comparing Version 1 ("1.0.1") and Version 2 ("1").

O(N + M) Level-wise Numeric Compare