Compare Version Numbers: Split and Compare Revisions
Compare Version Numbers is LeetCode 165. You are given two version strings version1 and version2. Each version string consists of revisions separated by dots. Compare them revision by revision from left to right. If one version string has fewer revisions than the other, treat the missing revisions as 0. Return -1 if version1 is smaller, 1 if it is larger, or 0 if they are equal.
Examples:
version1 = "1.2",version2 = "1.10"returns-1because revision 1 gives2vs10, and2is less than10.version1 = "1.01",version2 = "1.001"returns0because leading zeros do not matter:int("01")andint("001")are both1.version1 = "1.0",version2 = "1.0.0"returns0because the missing third revision ofversion1is treated as0, matchingversion2.
The approach
The core idea is to split each version string by ".", convert each revision to an integer, and compare them one at a time.
Here is the process:
- Split both version strings on
"."to get arrays of revision strings - Walk through both arrays simultaneously, index by index
- At each index, convert both revisions to integers. If one array is shorter, use
0for the missing revision - If the two integers differ, return
-1or1depending on which is smaller - If you finish all revisions without finding a difference, return
0
Converting to integers handles leading zeros automatically. int("001") is just 1, so "1.001" and "1.1" are treated as equal. The zero-padding for missing revisions handles different lengths, so "1.0" and "1.0.0" compare as equal too.
You do not need to explicitly pad the shorter array with zeros. Instead, when an index is past the end of one array, just use 0 as the value. This avoids creating any extra data and keeps the code clean.
The solution
def compareVersion(version1: str, version2: str) -> int:
v1 = version1.split(".")
v2 = version2.split(".")
for i in range(max(len(v1), len(v2))):
n1 = int(v1[i]) if i < len(v1) else 0
n2 = int(v2[i]) if i < len(v2) else 0
if n1 < n2:
return -1
if n1 > n2:
return 1
return 0
Walkthrough
Let's trace through version1 = "1.0" and version2 = "1.0.0". This is the trickiest example because the versions have different numbers of revisions but are still equal.
Step 1: Split by "."
version1 "1.0" splits into ["1", "0"]. version2 "1.0.0" splits into ["1", "0", "0"]. version2 has one more revision.
Step 2: Compare revision 0
int("1") == int("1"). Equal so far. Move to the next revision.
Step 3: Compare revision 1
int("0") == int("0"). Still equal. Move to revision 2.
Step 4: Compare revision 2 (padded)
version1 has no revision 2, so treat it as 0. int(0) == int("0"). Equal. No more revisions.
Step 5: Result
All revisions matched. Return 0 (versions are equal).
The algorithm compared all three revision positions (padding version1 with a virtual zero at index 2) and found them all equal. That trailing .0 in version2 made no difference because the padded zero matched it exactly.
Complexity analysis
Time: O(m + n) where m is the length of version1 and n is the length of version2. The split(".") call scans each string once. The comparison loop runs at most max(r1, r2) iterations where r1 and r2 are the revision counts, which is bounded by the string lengths.
Space: O(m + n) for storing the two arrays of revision strings after splitting. Each character in the original string ends up in exactly one element of the split array.
| Aspect | Value |
|---|---|
| Time | O(m + n) |
| Space | O(m + n) |
| Difficulty | Medium |
Building blocks
This problem is built from two fundamental techniques that appear across many string problems.
String splitting
The entire problem hinges on splitting a string by a delimiter. version1.split(".") turns "1.0.3" into ["1", "0", "3"]. This same operation appears in problems involving IP addresses, file paths, CSV parsing, and any structured string format. Knowing how split works, including that consecutive delimiters produce empty strings in some languages, is essential.
Padded comparison
When two sequences have different lengths, you treat the missing elements as a default value (here, 0) and continue comparing. This is the same idea used in Add Binary, Add Two Numbers, and Merge Sorted Arrays. The pattern is always the same: iterate up to the maximum length of the two sequences, and when one is exhausted, substitute the identity element.
Edge cases
Trailing zeros. "1.0" vs "1.0.0" should return 0. The extra .0 revisions contribute nothing because they are treated as 0, matching the padded zeros from the shorter version.
Leading zeros in a revision. "1.01" vs "1.001" should return 0. Converting "01" and "001" to integers both give 1. The int() call strips leading zeros automatically.
Different lengths with a real difference. "1.0.1" vs "1.0" should return 1. When you reach revision 2, version1 has 1 while version2 is padded to 0. Since 1 is greater than 0, you return 1.
Equal versions. "1.0" vs "1.0" returns 0. The loop compares each revision, finds them equal, and falls through to the final return 0.
From understanding to recall
This solution looks simple, and it is. But simplicity is deceptive when it comes to recall. Under interview pressure, the details matter: remembering to iterate up to max(len(v1), len(v2)) rather than min, using int() to handle leading zeros, defaulting to 0 when one array is shorter. Miss any one of those and the solution breaks on edge cases.
Spaced repetition locks in these details. You type the solution from scratch, verify it, and revisit it a few days later. Each repetition reinforces the specific choices that make the code correct: the max in the loop bound, the conditional int(v1[i]) if i < len(v1) else 0, and the three-way return logic. After a few reps, the pattern is automatic.
Related posts
- Longest Common Prefix - Another string comparison problem that involves scanning multiple strings position by position
- Add Binary - Uses the same padded comparison technique, walking two sequences of different lengths and substituting zeros for the shorter one