1. 项目概述
今天要分享的是LeetCode上的一道进制转换题目(编号3602),要求实现十六进制和三十六进制之间的相互转换。这道题看似基础,但涉及到位运算、字符处理、边界条件等多个编程基本功的考察点。我在实际解题过程中发现,很多人在处理这类进制转换问题时容易陷入一些思维误区,导致代码冗长或效率低下。
这道题的特殊之处在于三十六进制的表示范围(0-9, a-z)比常规的十六进制更广,需要考虑更多边界情况。我将通过手写实现的方式,带大家一步步拆解进制转换的核心逻辑,并分享几种不同语言下的优化解法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 进制转换原理剖析
2.1 进制转换的数学基础
进制转换本质上是对数值的重新表达。以十进制数N转换为k进制为例,其数学原理可以表示为:
N = a₀ × k⁰ + a₁ × k¹ + ... + aₙ × kⁿ
其中aᵢ ∈ [0, k-1]。转换过程就是通过反复除法和取余运算求得各个系数aᵢ的过程。
对于三十六进制,我们需要处理:
- 数字0-9(对应十进制0-9)
- 字母a-z(对应十进制10-35)
注意:在实际编码时要特别注意大小写问题,题目通常要求小写字母表示10-35
2.2 特殊进制处理的难点
相比常见的二进制、八进制、十六进制,三十六进制有以下特点:
- 字符映射范围更广(36个字符)
- 需要处理数字和字母的混合表示
- 大数运算时容易溢出(特别是反向转换时)
- 需要考虑前导零和空字符串等边界情况
3. 手写实现详解
3.1 基础实现方案
我们先看一个Python的参考实现:
python复制def hex_to_36(num_str):
# 十六进制转十进制
decimal = int(num_str, 16)
# 十进制转三十六进制
if decimal == 0:
return "0"
digits = []
while decimal > 0:
remainder = decimal % 36
if remainder < 10:
digits.append(str(remainder))
else:
digits.append(chr(ord('a') + remainder - 10))
decimal = decimal // 36
return ''.join(reversed(digits))
这个实现虽然直观,但有几个可以优化的点:
- 直接使用int()函数转换可能失去算法练习的意义
- 没有处理输入为空或非法字符的情况
- 反向转换(36转16)的逻辑没有实现
3.2 完全手写实现
下面我们完全手写转换逻辑,不使用内置的int()函数:
python复制def char_to_value(c):
if c.isdigit():
return int(c)
return 10 + ord(c.lower()) - ord('a')
def value_to_char(v):
if v < 10:
return str(v)
return chr(ord('a') + v - 10)
def hex_to_dec(hex_str):
decimal = 0
for c in hex_str:
decimal = decimal * 16 + char_to_value(c)
return decimal
def dec_to_36(decimal):
if decimal == 0:
return "0"
digits = []
while decimal > 0:
remainder = decimal % 36
digits.append(value_to_char(remainder))
decimal = decimal // 36
return ''.join(reversed(digits))
def hex_to_36(hex_str):
decimal = hex_to_dec(hex_str)
return dec_to_36(decimal)
这个实现完整展示了进制转换的每个步骤,更适合理解底层原理。
4. 性能优化与边界处理
4.1 大数处理优化
当处理很长的十六进制字符串时,可能会遇到整数溢出的问题。我们可以采用分段处理的方式:
python复制def hex_to_dec_large(hex_str):
decimal = 0
max_int = 2**63 - 1 # 防止溢出
for c in hex_str:
new_val = decimal * 16 + char_to_value(c)
if new_val > max_int:
raise ValueError("Number too large")
decimal = new_val
return decimal
4.2 边界条件处理
完善的实现应该处理以下边界情况:
- 空字符串输入
- 非法字符(如'g'在十六进制中无效)
- 前导零的处理
- 大小写混合输入
python复制def validate_hex(hex_str):
if not hex_str:
raise ValueError("Empty string")
valid_chars = set('0123456789abcdefABCDEF')
for c in hex_str:
if c not in valid_chars:
raise ValueError(f"Invalid hex character: {c}")
5. 反向转换实现
实现三十六进制转十六进制的逻辑:
python复制def thirty6_to_dec(thirty6_str):
decimal = 0
for c in thirty6_str:
decimal = decimal * 36 + char_to_value(c)
return decimal
def dec_to_hex(decimal):
if decimal == 0:
return "0"
digits = []
while decimal > 0:
remainder = decimal % 16
digits.append(value_to_char(remainder))
decimal = decimal // 16
return ''.join(reversed(digits))
def thirty6_to_hex(thirty6_str):
decimal = thirty6_to_dec(thirty6_str)
return dec_to_hex(decimal)
6. 测试用例设计
完整的实现需要全面的测试覆盖:
python复制test_cases = [
("0", "0"), # 边界:零
("a", "a"), # 十六进制最小字母
("1a", "q"), # 常规转换
("7fffffff", "1z141z3"), # 大数测试
("ffffffff", "1z141z3"), # 无符号最大值
]
for hex_str, expected in test_cases:
result = hex_to_36(hex_str)
assert result == expected, f"{hex_str} -> {result} (expected {expected})"
7. 不同语言的实现差异
7.1 C++实现要点
cpp复制#include <algorithm>
#include <string>
#include <stdexcept>
using namespace std;
int charToValue(char c) {
if (isdigit(c)) return c - '0';
if (isalpha(c)) return tolower(c) - 'a' + 10;
throw invalid_argument("Invalid character");
}
char valueToChar(int v) {
if (v < 10) return '0' + v;
return 'a' + (v - 10);
}
string hexTo36(const string& hexStr) {
long long decimal = 0;
for (char c : hexStr) {
decimal = decimal * 16 + charToValue(c);
}
if (decimal == 0) return "0";
string result;
while (decimal > 0) {
int remainder = decimal % 36;
result.push_back(valueToChar(remainder));
decimal /= 36;
}
reverse(result.begin(), result.end());
return result;
}
7.2 Java实现注意事项
Java实现时需要特别注意:
- 使用StringBuilder代替字符串拼接
- 处理可能的大数(考虑使用BigInteger)
- 严格的异常处理
java复制public static String hexTo36(String hexStr) {
BigInteger decimal = new BigInteger(hexStr, 16);
if (decimal.equals(BigInteger.ZERO)) {
return "0";
}
StringBuilder sb = new StringBuilder();
BigInteger base = BigInteger.valueOf(36);
while (decimal.compareTo(BigInteger.ZERO) > 0) {
BigInteger[] divRem = decimal.divideAndRemainder(base);
int remainder = divRem[1].intValue();
sb.append(valueToChar(remainder));
decimal = divRem[0];
}
return sb.reverse().toString();
}
8. 常见问题与调试技巧
8.1 典型错误排查
-
字符映射错误:
- 检查a-z是否对应10-35
- 确保大小写处理一致
-
前导零问题:
- 输入"0"应该输出"0"
- 输入"000a"应该等同于"a"
-
大数溢出:
- 测试接近数据类型上限的值
- 考虑使用大数类型
8.2 调试技巧
-
打印中间结果:
python复制print(f"Processing {c}, current decimal: {decimal}") -
单元测试:
- 为每个辅助函数编写测试
- 边界值测试(空串、单字符、最大长度)
-
性能分析:
python复制import timeit timeit.timeit('hex_to_36("7fffffff")', setup='from __main__ import hex_to_36')
9. 算法复杂度分析
-
时间复杂度:
- 十六进制转十进制:O(n)
- 十进制转三十六进制:O(log₃₆N)
- 总体:O(n + logN)
-
空间复杂度:
- 存储结果字符串:O(log₃₆N)
- 临时变量:O(1)
-
优化方向:
- 可以尝试直接转换而不经过十进制中间表示
- 对于超长字符串,采用分段处理
10. 实际应用场景
虽然三十六进制不常见,但这类算法在以下场景有实际应用:
- 短URL生成系统
- 文件校验码表示
- 数据库键压缩
- 特定编码需求(如Base36编码)
理解这种通用进制转换方法,可以帮助你快速适应各种自定义编码需求。我在实际工作中就遇到过需要将时间戳编码为更短字符串的需求,类似的进制转换思想非常有用。
