A clear explanation of converting a positive integer into an Excel column title using bijective base 26.
Problem Restatement
We are given a positive integer:
columnNumberWe need to return the corresponding Excel column title.
Excel columns are labeled like this:
| Number | Title |
|---|---|
1 | "A" |
2 | "B" |
26 | "Z" |
27 | "AA" |
28 | "AB" |
701 | "ZY" |
The problem asks us to convert an integer into this Excel-style column label. The constraint is 1 <= columnNumber <= 2^31 - 1.
Input and Output
| Item | Meaning |
|---|---|
| Input | Positive integer columnNumber |
| Output | Excel column title as a string |
| Alphabet | Uppercase letters A through Z |
| Important detail | There is no zero digit |
Example function shape:
def convertToTitle(columnNumber: int) -> str:
...Examples
Example 1:
columnNumber = 1Output:
"A"Example 2:
columnNumber = 26Output:
"Z"Example 3:
columnNumber = 28The title is:
"AB"Example 4:
columnNumber = 701The title is:
"ZY"First Thought: Base 26
This looks like base conversion.
In normal base 26, digits would be:
0, 1, 2, ..., 25But Excel columns use:
A, B, C, ..., Zwhere:
A = 1
B = 2
...
Z = 26There is no digit for zero.
That means this is not ordinary base 26. It is a 1-indexed, or bijective, base-26 system.
The fix is simple: subtract 1 before taking modulo 26.
Key Insight
For each character, do:
columnNumber -= 1
remainder = columnNumber % 26Now the remainder is between 0 and 25.
We can map it to a letter:
0 -> "A"
1 -> "B"
...
25 -> "Z"Then divide by 26:
columnNumber //= 26This extracts the title from right to left.
So we collect characters, then reverse them at the end.
Why We Subtract One
Consider:
columnNumber = 26If we use normal modulo:
26 % 26 = 0That would suggest "A" if we mapped 0 to "A", which is wrong.
The answer should be "Z".
So before modulo, use:
columnNumber -= 1Now:
25 % 26 = 25and 25 maps to "Z".
This same trick also handles 27:
27 - 1 = 26
26 % 26 = 0 -> "A"
26 // 26 = 1
1 - 1 = 0
0 % 26 = 0 -> "A"Collected from right to left, this gives:
"AA"Algorithm
Initialize an empty list:
chars = []While columnNumber > 0:
- Subtract
1fromcolumnNumber. - Compute
columnNumber % 26. - Convert that remainder to a letter.
- Append the letter to
chars. - Divide
columnNumberby26.
Finally, reverse chars and join it.
Walkthrough
Use:
columnNumber = 28First iteration:
columnNumber -= 1 # 27
remainder = 27 % 26 # 1
letter = "B"
columnNumber = 27 // 26 # 1Collected:
["B"]Second iteration:
columnNumber -= 1 # 0
remainder = 0 % 26 # 0
letter = "A"
columnNumber = 0 // 26 # 0Collected:
["B", "A"]Reverse:
["A", "B"]Return:
"AB"Correctness
The algorithm repeatedly extracts the last Excel digit.
Before each modulo operation, it subtracts 1, converting Excel’s 1-based digit system into a 0-based range from 0 to 25.
The remainder then uniquely determines the last letter of the current title.
After extracting that letter, integer division by 26 removes the last Excel digit and leaves the remaining prefix to be processed.
The loop continues until no prefix remains.
Because digits are extracted from right to left, reversing the collected letters gives the title in the correct left-to-right order.
Therefore, the algorithm returns the correct Excel column title.
Complexity
Let k be the number of letters in the output.
| Metric | Value | Why |
|---|---|---|
| Time | O(k) | Each loop produces one letter |
| Space | O(k) | We store the output letters |
Since columnNumber is at most 2^31 - 1, k is small.
Implementation
class Solution:
def convertToTitle(self, columnNumber: int) -> str:
chars = []
while columnNumber > 0:
columnNumber -= 1
remainder = columnNumber % 26
chars.append(chr(ord("A") + remainder))
columnNumber //= 26
chars.reverse()
return "".join(chars)Code Explanation
Create a list to store letters:
chars = []The loop runs until the whole number has been converted:
while columnNumber > 0:Convert from 1-based Excel digits to 0-based modulo digits:
columnNumber -= 1Find the current letter index:
remainder = columnNumber % 26Convert it to a character:
chars.append(chr(ord("A") + remainder))Move to the next higher digit:
columnNumber //= 26The letters were collected from right to left, so reverse them:
chars.reverse()Return the final title:
return "".join(chars)Recursive Implementation
The same logic can be written recursively:
class Solution:
def convertToTitle(self, columnNumber: int) -> str:
if columnNumber == 0:
return ""
columnNumber -= 1
prefix = self.convertToTitle(columnNumber // 26)
current = chr(ord("A") + columnNumber % 26)
return prefix + currentThe iterative version is usually preferable because it avoids recursion overhead.
Testing
def run_tests():
sol = Solution()
assert sol.convertToTitle(1) == "A"
assert sol.convertToTitle(2) == "B"
assert sol.convertToTitle(26) == "Z"
assert sol.convertToTitle(27) == "AA"
assert sol.convertToTitle(28) == "AB"
assert sol.convertToTitle(52) == "AZ"
assert sol.convertToTitle(53) == "BA"
assert sol.convertToTitle(701) == "ZY"
assert sol.convertToTitle(702) == "ZZ"
assert sol.convertToTitle(703) == "AAA"
print("all tests passed")
run_tests()| Test | Why |
|---|---|
1 | First column |
26 | End of one-letter range |
27 | First two-letter title |
28 | Standard example |
52 | Ends with Z |
53 | Carries into next leading letter |
701 | Standard larger example |
702 | "ZZ" boundary |
703 | First three-letter title |