#93
中等
高频
回溯复原IP地址
这是一道围绕字符串、排序展开的高频练习。建议先掌握「回溯」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
字符串
排序
题目分析
这道题表面上是在处理「复原IP地址」,但先要想清楚题目到底让你返回什么,以及过程中哪些约束必须一直满足。从题型上看,它主要在考 字符串、排序 这些能力。这类题更像在一棵决策树里向下展开,关键是递归时带什么状态、什么时候回退。只有先把题目要求翻译成人话,后面的推荐代码才是在实现思路,而不是直接给答案。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:用回溯依次切出 4 段,每段长度 1 到 3,且必须满足 IP 段的合法性规则。
推荐代码
推荐解法:回溯
时间复杂度: O(3^4)
空间复杂度: O(4)
核心思路: 用回溯依次切出 4 段,每段长度 1 到 3,且必须满足 IP 段的合法性规则。
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> result = new ArrayList<>();
backtrack(s, 0, new ArrayList<>(), result);
return result;
}
private void backtrack(String s, int start, List<String> path, List<String> result) {
// 终止条件:已经分割成4段且遍历完整个字符串
if (path.size() == 4) {
if (start == s.length()) {
result.add(String.join(".", path));
}
return;
}
// 遍历可能的分割位置
for (int i = 1; i <= 3; i++) {
// 超出字符串长度,终止循环
if (start + i > s.length()) {
break;
}
// 分割出当前段
String segment = s.substring(start, start + i);
// 检查当前段是否有效
if (isValidSegment(segment)) {
// 选择当前段
path.add(segment);
// 递归处理剩余部分
backtrack(s, start + i, path, result);
// 回溯,移除当前段
path.remove(path.size() - 1);
}
}
}
private boolean isValidSegment(String segment) {
// 长度为1-3
if (segment.length() == 0 || segment.length() > 3) {
return false;
}
// 不能有前导零,除非是单个零
if (segment.length() > 1 && segment.charAt(0) == '0') {
return false;
}
// 数值在0-255之间
int num = Integer.parseInt(segment);
return num >= 0 && num <= 255;
}
}结构化讲解
面试时怎么讲
开场思路
这题我会用回溯做字符串切分,段数固定为 4,非常适合剪枝。
核心过程
- 每层从当前位置尝试切出长度 1 到 3 的一段。
- 对每段先判断是否有前导零、是否超过 255。
- 合法就加入路径并递归下一段。
- 当路径恰好 4 段且字符串刚好用完时,收集当前 IP。
复杂度总结
时间复杂度近似常数级搜索,空间复杂度 O(4)。
面试补一句:这题的关键不在回溯本身,而在合法性剪枝是否及时。
核心思路
复原 IP 地址本质是有限深度的切分搜索。每层决定当前这一段取几个字符,合法就继续,不合法就立刻剪枝。
步骤讲解
1
每层尝试切出一段
从当前位置开始,尝试长度为 1 到 3 的片段作为当前 IP 段。
为什么这样做:每个 IP 段最大只能是三位数。
对应代码提示:for (int len = 1; len <= 3; len++) { ... }
2
先判断当前片段是否合法
片段不能有前导零,数值也必须在 0 到 255 之间。
为什么这样做:非法片段不可能组成合法 IP,应尽早剪枝。
对应代码提示:if (segment.startsWith("0") && segment.length() > 1) return;
3
切满四段且刚好用完字符串时收集答案
只有当段数等于 4 且所有字符都被用完时,当前路径才是合法 IP。
为什么这样做:段数不够或字符有剩余,都不符合 IP 地址结构。
对应代码提示:if (path.size() == 4 && index == s.length()) answer.add(String.join(".", path));
易错点
没处理前导零
像 01、00 都不是合法 IP 段。
正确理解:如果片段长度大于 1,就不能以
0 开头。段数到 4 后还继续切
会产生多余段数,结果结构错误。
正确理解:回溯中把段数作为强约束,超过 4 立即停止。
值超过 255 仍继续递归
这类分支不可能成功,只会浪费搜索。
正确理解:数值一旦超过 255 立即剪枝。
复杂度与适用判断
时间复杂度:O(3^4)
空间复杂度:O(4)
比其他方案更好在哪里:比暴力枚举所有点的位置再后验校验更自然,也更容易加剪枝。
适用判断:字符串切分题若目标段数固定且每段有明确合法规则,优先考虑回溯。
额外提醒
- 前导零和数值上限,是这题最重要的两条剪枝。
动画演示
如果你还没完全看懂,可以展开动画演示。
回溯动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
回溯动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
复原IP地址 - 回溯算法
回溯过程可视化
当前路径:
?
字符串分割:
2
5
5
2
5
5
1
1
1
3
5
当前分割位置:0
有效性验证:
当前段无效
有效IP地址:
操作日志
动画演示仅在桌面端提供,移动端请优先阅读推荐代码与结构化讲解。