LeetCode 补充题 22. IP 地址与整数的转换
题目描述
实现 IPv4 地址与无符号 32 位整数之间的相互转换。
对于地址
a.b.c.d,将四段分别视为一个 8 位无符号数,并按从高位到低位的顺序拼接,得到对应整数。反向转换时,按相同顺序拆分四个字节,返回点分十进制地址。
示例 1:
输入:ip = "10.0.3.193"
输出:167773121
示例 2:
输入:value = 167773121
输出:"10.0.3.193"
提示:
- 地址输入采用合法规范的 IPv4 点分十进制格式,四段均在
[0,255]范围内。 - 整数输入范围为
0 <= value <= 2^32 - 1。 - 第一段对应最高 8 位,最后一段对应最低 8 位;反向结果不包含多余前导零。
题意分析
IPv4 地址由四个十进制段组成,每段范围为
0..255,恰好占 8 位。把第一段放在最高 8 位、最后一段放在最低 8 位,就能将地址表示为一个无符号 32 位整数,再按相同顺序拆回地址。代码约定 IP 输入已是规范合法的四段地址,整数输入位于
[0, 2^32-1]。Java 用非负long保存这一范围,Go 直接使用uint32。
解法:按 8 位字节移位转换
核心思路
[!blue]
设四段为
a.b.c.d,对应数值为 $a\times256^3+b\times256^2+c\times256+d$。可以像读取十进制整数那样从左到右累积,只是每次乘的进制是 256:value = value * 256 + 当前段。左移 8 位就是乘 256,同时将最低 8 位清零。当前段不超过 255,只占最低 8 位,因此按位或与加法等价,代码使用
(value << 8) | 当前段。每处理一段,value都恰好表示已经读过的地址前缀;处理四段后就得到完整数值。反向转换时,对移位量
24、16、8、0依次右移,把目标字节移到最低 8 位,再用& 0xFF去掉它上方的所有位。四次提取分别恢复a、b、c、d,按十进制输出并用点连接,因此两种转换互为逆操作。
解题步骤
- IP 转整数:初始化
value = 0,按点拆分地址,从左到右解析四个十进制段。- 每读一段,先将已有结果左移 8 位,再按位或入当前段;全部处理后返回
value。- 整数转 IP:让
shift从 24 开始,每次减 8,直到处理完 0。- Java 用
(value >>> shift) & 0xFF,Go 用(value >> shift) & 0xFF提取字节,并依次拼接成四段地址。反向转换固定输出四段,不能在剩余数值为 0 时提前停止,否则会丢掉地址中的零段。最小数值 0 对应四个 0,最大数值
2^32-1对应四个 255,都使用相同流程。
代码实现
class Solution {
public long ipToInt(String ip) {
long value = 0;
for (String part : ip.split("\\.")) {
// 旧前缀提升一个字节,第一段最终位于最高八位。
value = (value << 8) | Integer.parseInt(part);
}
return value;
}
public String intToIP(long value) {
StringBuilder result = new StringBuilder();
for (int shift = 24; shift >= 0; shift -= 8) {
if (result.length() > 0) {
result.append('.');
}
// 右移后还需掩码,只保留当前一个字节。
result.append((value >>> shift) & 0xFF);
}
return result.toString();
}
}
import (
"strconv"
"strings"
)
func ipToInt(ip string) uint32 {
var value uint32
for _, part := range strings.Split(ip, ".") {
num, _ := strconv.Atoi(part)
// 旧前缀提升一个字节,第一段最终位于最高八位。
value = value<<8 | uint32(num)
}
return value
}
func intToIP(value uint32) string {
parts := make([]string, 0, 4)
for shift := 24; shift >= 0; shift -= 8 {
// 右移后还需掩码,只保留当前一个字节。
part := value >> shift & 0xFF
parts = append(parts, strconv.Itoa(int(part)))
}
return strings.Join(parts, ".")
}
复杂度分析
- 时间复杂度:$O(1)$。IPv4 固定为 4 段,字符串长度也有固定上限。
- 空间复杂度:$O(1)$。切分数组和输出缓冲区都只保存固定数量的字符与分段。
关键点总结
[!green]
- 正向每次腾出一个低位字节,再填入新段;先读入的段会被后续左移推到高位。
- 反向先移动目标字节,再屏蔽其他字节,不能只做右移。
- Java 的
long与 Go 的uint32都能完整保存0..4294967295,拼接过程中不会丢失最高位。
易错点总结
[!yellow]
- 字节顺序反了:第一段应放在最高 8 位;从右向左累加会得到颠倒的地址。
- Java 使用
int:255.255.255.255会溢出成-1,正确的无符号值是 4294967295。- Java 写成
split("."):点号会按正则中的任意字符匹配,无法得到四段。- 反向只右移、不掩码:中间两段会混入更高字节,得到远大于 255 的数。
- 移位漏掉 0:只处理 24、16、8 会缺少最后一段。
- 直接用于非法输入:段数和段值范围是上述推导的前提;Go 中忽略解析错误也依赖输入已经合法的约定。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 751. IP 到 CIDR | 中等 | IPv4转整数后可继续按二进制边界划分CIDR地址块,本题只做双向转换。 |
| 468. 验证IP地址 | 中等 | 原题负责格式合法性判断,本题约定规范合法IPv4输入,转换不等于同时实现IPv6校验。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!