CodeTop Top 300 热门题目10-验证IP地址

  1. 验证IP地址 https://leetcode.cn/problems/validate-ip-address/

给定一个字符串 queryIP。如果是有效的 IPv4 地址,返回 "IPv4" ;如果是有效的 IPv6 地址,返回 "IPv6" ;如果不是上述类型的 IP 地址,返回 "Neither" 。

有效的IPv4地址 是 "x1.x2.x3.x4" 形式的IP地址。 其中 0 <= xi <= 255 且 xi 不能包含 前导零。例如: "192.168.1.1" 、 "192.168.1.0" 为有效IPv4地址, "192.168.01.1" 为无效IPv4地址; "192.168.1.00" 、 "192.168@1.1" 为无效IPv4地址。

一个有效的IPv6地址 是一个格式为"x1:x2:x3:x4:x5:x6:x7:x8" 的IP地址,其中:

1 <= xi.length <= 4

xi 是一个 十六进制字符串 ,可以包含数字、小写英文字母( 'a' 到 'f' )和大写英文字母( 'A' 到 'F' )。

在 xi 中允许前导零。

例如 "2001:0db8:85a3:0000:0000:8a2e:0370:7334" 和 "2001:db8:85a3:0:0:8A2E:0370:7334" 是有效的 IPv6 地址,而 "2001:0db8:85a3::8A2E:037j:7334" 和 "02001:0db8:85a3:0000:0000:8a2e:0370:7334" 是无效的 IPv6 地址。

示例 1:

输入:queryIP = "172.16.254.1"

输出:"IPv4"

解释:有效的 IPv4 地址,返回 "IPv4"

示例 2:

输入:queryIP = "2001:0db8:85a3:0:0:8A2E:0370:7334"

输出:"IPv6"

解释:有效的 IPv6 地址,返回 "IPv6"

示例 3:

输入:queryIP = "256.256.256.256"

输出:"Neither"

解释:既不是 IPv4 地址,又不是 IPv6 地址

提示:

queryIP 仅由英文字母,数字,字符 '.' 和 ':' 组成。

好题!验证IP地址需要同时处理IPv4和IPv6两种格式,我给你完整解析。

题目理解

复制代码
输入:"172.16.254.1"  → 输出:"IPv4"
输入:"2001:0db8:85a3:0:8A2E:0370:7334" → 输出:"IPv6"
输入:"256.256.256.256" → 输出:"Neither"(超出范围)
输入:"1.1.1.1." → 输出:"Neither"(多余分隔符)

方法一:按协议分隔判断(推荐)

python 复制代码
def validIPAddress(queryIP: str) -> str:
    """
    核心思路:根据分隔符判断协议类型,分别验证
    
    时间复杂度:O(n),只遍历一次
    空间复杂度:O(1)
    """
    
    # ---------- IPv4 验证 ----------
    def isIPv4(s: str) -> bool:
        """
        IPv4 规则:
        1. 用 "." 分隔,必须恰好有4段
        2. 每段必须是数字
        3. 每段范围:0-255
        4. 不能有前导零(除了 "0" 本身)
           - "0" 合法
           - "00" 不合法
           - "01" 不合法
           - "255" 合法
        """
        # 检查段数
        parts = s.split('.')
        if len(parts) != 4:
            return False
        
        for part in parts:
            # 每段不能为空
            if not part:
                return False
            
            # 每段必须全是数字
            if not part.isdigit():
                return False
            
            # 不能有前导零:len("01") > 1
            # "0" 本身长度为1,可以;"00" 长度>1,不行
            if len(part) > 1 and part[0] == '0':
                return False
            
            # 数值范围检查
            num = int(part)
            if num < 0 or num > 255:
                return False
        
        return True
    
    # ---------- IPv6 验证 ----------
    def isIPv6(s: str) -> bool:
        """
        IPv6 规则:
        1. 用 ":" 分隔,必须恰好有8段
        2. 每段必须是1-4个十六进制字符
        3. 十六进制字符:0-9, a-f, A-F
        
        简化实现:直接尝试 int(..., 16) 转换
        """
        # 检查段数
        parts = s.split(':')
        if len(parts) != 8:
            return False
        
        # 十六进制字符集
        hex_chars = set("0123456789abcdefABCDEF")
        
        for part in parts:
            # 每段不能为空
            if not part:
                return False
            
            # 每段长度1-4
            if len(part) > 4 or len(part) < 1:
                return False
            
            # 每段必须是十六进制字符
            for c in part:
                if c not in hex_chars:
                    return False
        
        return True
    
    # ---------- 主逻辑 ----------
    # 先判断分隔符类型,快速筛选
    if '.' in queryIP and ':' not in queryIP:
        # 可能是 IPv4(IPv4用".",IPv6用":")
        return "IPv4" if isIPv4(queryIP) else "Neither"
    elif ':' in queryIP and '.' not in queryIP:
        # 可能是 IPv6
        return "IPv6" if isIPv6(queryIP) else "Neither"
    else:
        # 混合分隔符或不包含分隔符 → 非法
        return "Neither"


# 测试
test_cases = [
    "172.16.254.1",      # IPv4
    "256.256.256.256",   # Neither(超出范围)
    "1.1.1.1.",          # Neither(多余分隔符)
    ".1.1.1.1",          # Neither(前导分隔符)
    "192.168.1.1",       # IPv4
    "2001:0db8:85a3:0:8A2E:0370:7334",  # IPv6
    "2001:db8:85a3:0:8A2E:370:7334",    # IPv6
    "2001:0db8:85a3::8A2E:0370:7334",   # Neither(含::)
    "1.1.1.1.1",         # Neither(IPv4段数不对)
    "1:1:1:1:1:1:1:1::",  # Neither
    "1:1:1:1:1:1:1:1G",   # Neither(含非十六进制)
]

for ip in test_cases:
    print(f'"{ip}" → {validIPAddress(ip)}')

方法二:正则表达式(简洁但难理解)

python 复制代码
import re

def validIPAddress_regex(queryIP: str) -> str:
    """
    使用正则表达式验证
    
    IPv4 正则:
    - ^ 和 $:匹配整个字符串
    - (?:...):非捕获组
    - \d:数字
    - (?!.*\.):负向前瞻,确保没有其他点
    - (?!\d*$ ):确保不是纯数字结尾
    
    IPv6 正则:
    - [0-9a-fA-F]{1,4}:1-4个十六进制字符
    - (?::[0-9a-fA-F]{1,4}){7}:7组冒号+4个十六进制
    """
    # IPv4 正则:每段 1-3位数字,范围 0-255
    ipv4_pattern = r'^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$'
    
    # IPv6 正则:每段 1-4个十六进制字符
    ipv6_pattern = r'^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$'
    
    if re.match(ipv4_pattern, queryIP):
        return "IPv4"
    elif re.match(ipv6_pattern, queryIP):
        return "IPv6"
    else:
        return "Neither"


# IPv4 正则解释
"""
分步解析:^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$

每一段可能的匹配:
- 25[0-5]:250-255
- 2[0-4]\d:200-249(\d = 0-9)
- 1\d{2}:100-199
- [1-9]?\d:1-99(? 表示可选,所以也能匹配 0-9)

为什么这样设计?
- 避免匹配 "256"、"01" 等非法情况
- 用分支覆盖所有 0-255 的合法情况
"""

方法三:状态机(最严谨,展示算法能力)

python 复制代码
def validIPAddress_fsm(queryIP: str) -> str:
    """
    有限状态机解法
    
    状态转移图:
    ┌─────────────────────────────────────────────────────────┐
    │                                                         │
    │    ┌──────┐    "."    ┌──────┐    "."    ┌──────┐    "."    ┌──────┐    "."    ┌────────┐
    │ --→│ S0   │──────────→│ S1   │──────────→│ S2   │──────────→│ S3   │──────────→│ END    │
    │    │ 数字 │           │ 数字 │           │ 数字 │           │ 数字 │           │        │
    │    └──────┘           └──────┘           └──────┘           └──────┘           └────────┘
    │       │                  │                  │                  │                  │
    │       └──────────────────┴──────────────────┴──────────────────┴──────────────────┘
    │                                    检查合法性
    │
    │    ┌──────┐    ":"    ┌──────┐    ":"    ┌──────┐    ":"    ┌──────┐    ":"    ┌────────┐
    └--→ │ H0   │──────────→│ H1   │──────────→│ H2   │──────────→│ H7   │──────────→│ HEND   │
         │ 16进 │           │ 16进 │           │ 16进 │           │ 16进 │           │        │
         └──────┘           └──────┘           └──────┘           └──────┘           └────────┘
    
    S系列:IPv4 状态(S0→S1→S2→S3→END = 4段)
    H系列:IPv6 状态(H0→H1→...→H7→HEND = 8段)
    """
    
    def check_ipv4(s: str) -> bool:
        """
        状态机验证 IPv4
        """
        n = len(s)
        if n == 0:
            return False
        
        # 状态:当前段数字个数、当前段数值、段计数
        digit_count = 0      # 当前段的字符数
        current_num = 0      # 当前段的数值
        segment_count = 0    # 已完成的段数
        
        for i, c in enumerate(s):
            if c == '.':
                # 遇到分隔符:检查当前段是否合法
                if digit_count == 0:
                    # 空段,如 "1..2.3"
                    return False
                if current_num > 255:
                    return False
                if digit_count > 1 and s[i - digit_count] == '0':
                    # 有前导零,如 "01"
                    return False
                
                # 重置状态,准备下一段
                digit_count = 0
                current_num = 0
                segment_count += 1
            elif c.isdigit():
                # 累加数值(类似 parseInt)
                current_num = current_num * 10 + int(c)
                digit_count += 1
                
                # 提前剪枝:数值已超
                if current_num > 255:
                    return False
            else:
                # 遇到非法字符
                return False
        
        # 处理最后一段
        if digit_count == 0:
            return False
        if current_num > 255:
            return False
        if digit_count > 1 and s[-digit_count] == '0':
            return False
        
        # 必须是4段
        return segment_count == 3
    
    def check_ipv6(s: str) -> bool:
        """
        状态机验证 IPv6
        """
        n = len(s)
        if n == 0:
            return False
        
        hex_chars = set("0123456789abcdefABCDEF")
        char_count = 0  # 当前段字符数
        segment_count = 0  # 已完成的段数
        
        for i, c in enumerate(s):
            if c == ':':
                # 遇到分隔符:检查当前段是否合法
                if char_count == 0:
                    return False
                if char_count > 4:
                    return False
                
                char_count = 0
                segment_count += 1
            elif c in hex_chars:
                char_count += 1
                if char_count > 4:
                    return False
            else:
                return False
        
        # 处理最后一段
        if char_count == 0 or char_count > 4:
            return False
        
        # 必须是8段
        return segment_count == 7
    
    # 先根据分隔符判断协议类型
    if '.' in queryIP and ':' not in queryIP:
        return "IPv4" if check_ipv4(queryIP) else "Neither"
    elif ':' in queryIP and '.' not in queryIP:
        return "IPv6" if check_ipv6(queryIP) else "Neither"
    else:
        return "Neither"

完整测试

python 复制代码
if __name__ == "__main__":
    test_cases = [
        # IPv4 测试
        ("172.16.254.1", "IPv4"),
        ("192.168.1.1", "IPv4"),
        ("0.0.0.0", "IPv4"),
        ("255.255.255.255", "IPv4"),
        ("1.2.3.4", "IPv4"),
        
        # IPv4 非法情况
        ("256.256.256.256", "Neither"),   # 超出范围
        ("1.1.1.01", "Neither"),          # 前导零
        ("1.1.1.", "Neither"),            # 空段
        (".1.1.1", "Neither"),            # 前导分隔符
        ("1.1.1", "Neither"),             # 段数不足
        ("1.1.1.1.1", "Neither"),         # 段数过多
        ("1.1.1.1.", "Neither"),          # 多余分隔符
        ("1.1.1.1.1.1", "Neither"),       # 段数过多
        ("01.1.1.1", "Neither"),          # 前导零
        ("1.2.3.4.5", "Neither"),         # 段数过多
        
        # IPv6 测试
        ("2001:0db8:85a3:0:8A2E:0370:7334", "IPv6"),
        ("2001:0db8:85a3:0000:0000:8A2E:0370:7334", "IPv6"),
        ("1:1:1:1:1:1:1:1", "IPv6"),
        ("1234:abcd:1234:abcd:1234:abcd:1234:abcd", "IPv6"),
        ("::1", "Neither"),               # 简写形式不处理
        
        # IPv6 非法情况
        ("1:1:1:1:1:1:1:1:", "Neither"),  # 尾部冒号
        ("1:1:1:1:1:1:1:1G", "Neither"),  # 含非法字符
        ("1:1:1:1:1:1:1:1:1", "Neither"), # 段数过多
        ("1:1:1:1:1:1:1", "Neither"),     # 段数不足
        ("1:1:1:1:1:1:1", "Neither"),     # 段数不足
        
        # 混合情况
        ("1.1.1.1:1", "Neither"),         # 混合分隔符
        ("::", "Neither"),                 # IPv6 简写
        ("", "Neither"),                   # 空字符串
    ]
    
    print("=" * 60)
    print("方法一:分隔符判断")
    print("=" * 60)
    for ip, expected in test_cases:
        result = validIPAddress(ip)
        status = "✓" if result == expected else "✗"
        print(f'{status} "{ip:45}" → {result:8} (期望: {expected})')
    
    print("\n" + "=" * 60)
    print("方法二:正则表达式")
    print("=" * 60)
    for ip, expected in test_cases:
        result = validIPAddress_regex(ip)
        status = "✓" if result == expected else "✗"
        print(f'{status} "{ip:45}" → {result:8} (期望: {expected})')

关键知识点图解

IPv4 验证要点

复制代码
合法 IPv4 示例:"192.168.1.1"

检查清单:
┌─────────────────────────────────────────────────────┐
│ 1. 段数 = 4 ✓                                       │
│ 2. 每段 = 数字 ✓                                    │
│ 3. 每段范围 0-255 ✓                                 │
│ 4. 无前导零 ✓                                       │
│    - "0" ✓                                          │
│    - "00" ✗                                         │
│    - "01" ✗                                         │
│    - "192" ✓                                        │
└─────────────────────────────────────────────────────┘

常见错误:
┌────────────────┬────────────────────┐
│ "256.1.1.1"    │ 数值超 255         │
│ "1.01.1.1"     │ 前导零             │
│ "1.1..1.1"     │ 空段               │
│ "1.1.1"        │ 段数不足           │
│ "1.1.1.1.1"    │ 段数过多           │
└────────────────┴────────────────────┘

IPv6 验证要点

复制代码
合法 IPv6 示例:"2001:0db8:85a3:0000:8A2E:0370:7334"

检查清单:
┌─────────────────────────────────────────────────────┐
│ 1. 段数 = 8 ✓                                       │
│ 2. 每段 = 1-4个十六进制字符 ✓                       │
│ 3. 字符集:0-9, a-f, A-F ✓                          │
└─────────────────────────────────────────────────────┘

十六进制字符:
┌──────┬────────────────────────────────┐
│ 数字 │ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9   │
│ 小写 │ a, b, c, d, e, f               │
│ 大写 │ A, B, C, D, E, F               │
└──────┴────────────────────────────────┘

注意:本题不处理 IPv6 简写形式(如 "::1"、"2001::1")

面试建议

方法 推荐度 适用场景
分隔符判断 ⭐⭐⭐⭐⭐ 面试首选,逻辑清晰
正则表达式 ⭐⭐⭐ 简洁但需要记住正则
状态机 ⭐⭐⭐ 展示算法能力

回答要点

  1. 先根据分隔符判断协议类型
  2. 重点讲清楚 IPv4 的前导零问题
  3. 提到可以用正则简化,但现场手写分隔判断更稳妥
相关推荐
小白学大数据1 小时前
Python 3.7 高并发爬虫:接口请求与页面解析并发处理
开发语言·爬虫·python
样例过了就是过了1 小时前
LeetCode热题100 乘积最大子数组
c++·算法·leetcode·动态规划
Jacky-0081 小时前
Python pywin32 outlook邮箱
开发语言·python·outlook
minji...1 小时前
Linux 线程同步与互斥(六) 线程安全与重入问题,死锁,线程done
linux·运维·开发语言·数据库·c++·算法·安全
wayz111 小时前
Day 16 编程实战:PCA主成分分析与技术指标降维
人工智能·算法·机器学习
探序基因1 小时前
单细胞转录组Seurat去批次-RPCA算法
算法
yuanyuan2o21 小时前
GDB 调试指南
c语言·c++·算法
2401_873479401 小时前
遭遇DDoS攻击后如何快速分析攻击源?用IP查询+离线库定位异常IP
服务器·开发语言·tcp/ip·php
流年如夢1 小时前
算法效率:复杂度原理解析
c语言·数据结构·算法