怎么把自己的网站推广,wordpress 自动提交,wordpress自动把内容变成图片,用word文档做网站文章目录LeetCode 468. 验证IP地址 - 详细解析题目描述IPv4验证规则#xff1a;IPv6验证规则#xff1a;最优Java解决方案#xff08;注释完整版#xff09;关键变量含义及代码技巧代码技巧详解1. 前导零检查的最佳实践2. IPv6为什么不能用Character.isDigit()3. 针对性注释…
文章目录LeetCode 468. 验证IP地址 - 详细解析题目描述IPv4验证规则IPv6验证规则最优Java解决方案注释完整版关键变量含义及代码技巧代码技巧详解1. 前导零检查的最佳实践2. IPv6为什么不能用Character.isDigit()3. 针对性注释设计算法核心思路可视化演示过程测试用例1有效IPv4 - 172.16.254.1优化版演示测试用例2有效IPv6 - 2001:0db8:85a3:0:0:8A2E:0370:7334优化版演示测试用例3无效IPv4前导零- 192.168.01.1优化版演示测试用例4无效IPv4超出范围- 256.256.256.256优化版演示测试用例5无效IPv6段过长- 02001:0db8:85a3:0000:0000:8a2e:0370:7334测试用例6无效格式非法字符- 192.1681.1算法复杂度分析关键技术点详解String的isEmpty() vs isBlank()方法对比详细对比示例在IP验证中的应用安全的null检查split() 方法的 limit 参数代码优化技巧总结代码优势对比常见陷阱LeetCode 468. 验证IP地址 - 详细解析
题目描述
给定一个字符串 queryIP。如果是有效的 IPv4 地址返回 “IPv4” 如果是有效的 IPv6 地址返回 “IPv6” 如果不是上述类型的 IP 地址返回 “Neither” 。
IPv4验证规则
格式“x1.x2.x3.x4”0 xi 255xi 不能包含前导零除了0本身
IPv6验证规则
格式“x1:x2:x3:x4:x5:x6:x7:x8”1 xi.length 4xi 是十六进制字符串0-9, a-f, A-F允许前导零
最优Java解决方案注释完整版
class Solution {public String validIPAddress(String queryIP) {if (queryIP.contains(.)) {return isValidIPV4(queryIP) ? IPv4 : Neither;} else if (queryIP.contains(:)) {return isValidIPV6(queryIP) ? IPv6 : Neither;} else return Neither;}boolean isValidIPV4(String ip) {String[] parts ip.split(\\., -1);if (parts.length ! 4) {return false;}for (String part : parts) {int n part.length();if (n 0 || n 3) { // 针对和2555的输入return false;}if (n 1 part.startsWith(0)) { // 针对10.01.1.1输入前导零return false;}for (char c : part.toCharArray()) { // 针对非数字输入if (!Character.isDigit(c)) {return false;}}int num Integer.parseInt(part); // 针对256.0.0.1输入if (!(num 0 num 255)) {return false;}}return true;}boolean isValidIPV6(String ip) {String[] parts ip.split(:, -1);if (parts.length ! 8) {return false;}for (String part : parts) {int n part.length();if (n 0 || n 4) { // 针对空串和11111输入return false;}for (char c : part.toCharArray()) {// 这里不能替换为Character.isDigit(c)if (!((c 0 c 9) ||(c a c f) ||(c A c F))) {return false;}}}return true;}
}关键变量含义及代码技巧
变量名含义作用queryIP输入的待验证IP字符串算法的输入参数parts分割后的IP段数组存储按.“或”:分割的各个部分part单个IP段字符串用于验证每个独立的IP段nIP段的长度int n part.length() 提高代码可读性numIPv4段的整数值用于检查IPv4段是否在0-255范围内c字符变量用于逐字符检查是否符合格式要求
代码技巧详解
1. 前导零检查的最佳实践
if (n 1 part.startsWith(0)) { // 针对10.01.1.1输入前导零return false;
}为什么用 startsWith(0) 而不是 charAt(0) 0
更语义化表达以0开头的意图更清晰startsWith() 内部已经做了边界检查更安全
2. IPv6为什么不能用Character.isDigit()
// 这里不能替换为Character.isDigit(c)
if (!((c 0 c 9) ||(c a c f) ||(c A c F))) {return false;
}原因分析
Character.isDigit(c) 只检查数字字符(0-9)IPv6需要十六进制字符数字(0-9) 字母(a-f, A-F)必须手动检查三个范围数字、小写字母、大写字母
3. 针对性注释设计
代码中的注释都标明了具体要处理的边界情况
// 针对和2555的输入 → 长度检查// 针对10.01.1.1输入前导零 → 前导零检查// 针对非数字输入 → 字符合法性检查// 针对256.0.0.1输入 → 数值范围检查// 针对空串和11111输入 → IPv6长度检查
算法核心思路
预判断通过检查是否包含.“或”:来初步判断IP类型分割验证将IP按分隔符分割成段验证段数是否正确逐段检查对每个段进行格式和数值范围验证字符级验证确保每个字符都符合对应IP类型的要求
可视化演示过程
测试用例1有效IPv4 - “172.16.254.1”优化版演示
步骤1初步判断
queryIP 172.16.254.1
包含. ✓ → 进入IPv4验证步骤2分割并长度检查
parts [172, 16, 254, 1]
parts.length 4 ✓ → 段数正确继续验证步骤3逐段内联验证
part[0] 172
├── 长度检查: 3 ≤ 3 ✓
├── 前导零检查: 首字符1 ≠ 0 ✓
├── 字符检查: 1,7,2 全为数字 ✓
└── 数值检查: Integer.parseInt(172) 172 ≤ 255 ✓part[1] 16
├── 长度检查: 2 ≤ 3 ✓
├── 前导零检查: 首字符1 ≠ 0 ✓
├── 字符检查: 1,6 全为数字 ✓
└── 数值检查: Integer.parseInt(16) 16 ≤ 255 ✓part[2] 254
├── 长度检查: 3 ≤ 3 ✓
├── 前导零检查: 首字符2 ≠ 0 ✓
├── 字符检查: 2,5,4 全为数字 ✓
└── 数值检查: Integer.parseInt(254) 254 ≤ 255 ✓part[3] 1
├── 长度检查: 1 ≤ 3 ✓
├── 前导零检查: 长度1无需检查 ✓
├── 字符检查: 1 为数字 ✓
└── 数值检查: Integer.parseInt(1) 1 ≤ 255 ✓结果返回 IPv4测试用例2有效IPv6 - “2001:0db8:85a3:0:0:8A2E:0370:7334”优化版演示
步骤1初步判断
queryIP 2001:0db8:85a3:0:0:8A2E:0370:7334
不包含. 但包含: ✓ → 进入IPv6验证步骤2分割并长度检查
parts [2001, 0db8, 85a3, 0, 0, 8A2E, 0370, 7334]
parts.length 8 ✓ → 段数正确继续验证步骤3逐段内联验证
part[0] 2001
├── 长度检查: 4 ≤ 4 ✓
└── 内联十六进制检查:│ 2: 0≤2≤9 ✓│ 0: 0≤0≤9 ✓│ 0: 0≤0≤9 ✓│ 1: 0≤1≤9 ✓part[1] 0db8
├── 长度检查: 4 ≤ 4 ✓
└── 内联十六进制检查:│ 0: 0≤0≤9 ✓│ d: a≤d≤f ✓│ b: a≤b≤f ✓│ 8: 0≤8≤9 ✓part[5] 8A2E
├── 长度检查: 4 ≤ 4 ✓
└── 内联十六进制检查:│ 8: 0≤8≤9 ✓│ A: A≤A≤F ✓│ 2: 0≤2≤9 ✓│ E: A≤E≤F ✓... (其他段类似验证) ...结果返回 IPv6测试用例3无效IPv4前导零- “192.168.01.1”优化版演示
步骤1初步判断
queryIP 192.168.01.1
包含. ✓ → 进入IPv4验证步骤2分割并长度检查
parts [192, 168, 01, 1]
parts.length 4 ✓ → 段数正确继续验证步骤3逐段内联验证
part[0] 192 ✓ (验证通过)
part[1] 168 ✓ (验证通过)
part[2] 01
├── 长度检查: 2 ≤ 3 ✓
├── 前导零检查: 长度1 且 首字符0 ✗
└── 立即返回false无需进行后续计算结果返回 Neither测试用例4无效IPv4超出范围- “256.256.256.256”优化版演示
步骤1初步判断
queryIP 256.256.256.256
包含. ✓ → 进入IPv4验证步骤2分割并长度检查
parts [256, 256, 256, 256]
parts.length 4 ✓ → 段数正确继续验证步骤3逐段内联验证
part[0] 256
├── 长度检查: 3 ≤ 3 ✓
├── 前导零检查: 首字符2 ≠ 0 ✓
├── 字符检查: 2,5,6 全为数字 ✓
└── 数值检查: Integer.parseInt(256) 256 255 ✗结果返回 Neither测试用例5无效IPv6段过长- “02001:0db8:85a3:0000:0000:8a2e:0370:7334”
步骤1初步判断
queryIP 02001:0db8:85a3:0000:0000:8a2e:0370:7334
不包含. 但包含: ✓ → 可能是IPv6步骤2分割IP
parts [02001, 0db8, 85a3, 0000, 0000, 8a2e, 0370, 7334]
parts.length 8 ✓ → 段数正确步骤3逐段验证
part[0] 02001
├── 长度检查: 5 4 ✗
└── 验证失败结果返回 Neither测试用例6无效格式非法字符- “192.1681.1”
步骤1初步判断
queryIP 192.1681.1
包含. ✓ → 可能是IPv4步骤2分割IP
parts [192, 1681, 1]
parts.length 3 ≠ 4 ✗结果返回 Neither算法复杂度分析
时间复杂度O(n)其中n是字符串长度。需要遍历整个字符串进行分割和验证空间复杂度O(1)除了存储分割后的段数组不需要额外空间
关键技术点详解
String的isEmpty() vs isBlank()方法对比
在IP验证中我们经常需要检查字符串是否为空了解这两个方法的区别很重要
方法作用Java版本检查内容isEmpty()检查长度是否为0Java 6只检查 length() 0isBlank()检查是否为空或只有空白字符Java 11检查 isEmpty() || 全为空白字符
详细对比示例
String str1 ; // 空字符串
String str2 ; // 一个空格
String str3 \t\n ; // 多个空白字符(空格、制表符、换行符)
String str4 a ; // 包含非空白字符
String str5 null; // null值// isEmpty() 结果
str1.isEmpty() → true ✓ (长度为0)
str2.isEmpty() → false ✗ (长度为1)
str3.isEmpty() → false ✗ (长度为4)
str4.isEmpty() → false ✗ (长度为3)
// str5.isEmpty() → NullPointerException!// isBlank() 结果 (Java 11)
str1.isBlank() → true ✓ (长度为0)
str2.isBlank() → true ✓ (只有空白字符)
str3.isBlank() → true ✓ (只有空白字符)
str4.isBlank() → false ✗ (包含非空白字符)
// str5.isBlank() → NullPointerException!在IP验证中的应用
// 检查IP段是否为空的不同方式
private boolean isValidIPv4Part(String part) {// 方式1直接检查长度推荐if (part.length() 0) return false;// 方式2使用isEmpty()等效if (part.isEmpty()) return false;// 方式3使用isBlank()Java 11更严格if (part.isBlank()) return false; // 会拒绝 这样的空格段// ... 其他验证逻辑
}安全的null检查
// 推荐的安全检查方式
public static boolean isNullOrEmpty(String str) {return str null || str.isEmpty();
}public static boolean isNullOrBlank(String str) {return str null || str.isBlank(); // Java 11
}split() 方法的 limit 参数
String[] parts ip.split(\\., -1);split(regex, limit) 中的 limit 参数控制分割行为
limit值行为示例limit 0最多分割成limit个部分a.b.c.split(\\., 2) → [a, b.c]limit 0默认行为移除尾部空字符串a.b..split(\\.) → [a, b]limit 0保留所有空字符串包括尾部a.b..split(\\., -1) → [a, b, ]
为什么IP验证必须用 -1
// 测试案例对比
String ip1 192.168.1.; // 末尾多点号
String ip2 192..168.1; // 连续点号// 不使用-1 (默认行为)
ip1.split(\\.) → [192, 168, 1] // 长度3错误应该是4段
ip2.split(\\.) → [192, , 168, 1] // 长度4但漏掉了空段检测// 使用-1 (正确行为)
ip1.split(\\., -1) → [192, 168, 1, ] // 长度4能检测到末尾空段
ip2.split(\\., -1) → [192, , 168, 1] // 长度4能检测到中间空段代码优化技巧总结
关键的 split(-1)确保捕获所有边界情况包括末尾和中间的空段长度预判断直接检查分割后数组长度不符合直接返回false语义化变量命名使用 int n part.length() 提高代码可读性语义化方法调用使用 part.startsWith(0) 替代 part.charAt(0) 0详细的针对性注释每个检查都注明具体处理的边界情况精确的字符范围检查IPv6手动检查三个字符范围不使用Character.isDigit()清晰的条件表达式使用 !(num 0 num 255) 明确表达范围检查
代码优势对比
优化项传统写法当前实现优势说明变量命名part.length() 重复调用int n part.length()提高可读性减少重复计算前导零检查part.charAt(0) 0part.startsWith(0)语义更清晰表达意图更直接注释设计简单功能注释针对性边界情况注释明确每个检查要处理的具体问题字符范围检查使用库函数或正则手动三范围检查IPv6需求下更精确避免误判条件表达式num 0 || num 255!(num 0 num 255)逻辑更直观表达不在范围内长度预检查在循环中检查预先检查数组长度提早退出避免无效处理
常见陷阱
忘记检查前导零IPv4未正确处理空段如连续分隔符字符范围检查不全面IPv6的大小写字母数值范围检查遗漏边界值